Data Cleaning

#loading in the Catapult data to look at sprinting values
Catapult_Session <- read_csv("data-sets/data-sets-uncompressed/data-sets-compressed/Running Imbalance and Speed/Catapult Session - Outdoor FB.csv")

#loading in the Historical Running data to look at running imbalance values
Historical_Running <- read_csv("data-sets/data-sets-uncompressed/data-sets-compressed/Running Imbalance and Speed/Compiled Historical Running Imbalance FB.csv")

#loading in the Incident Report to look at HSIs
Incident_Report <- read_csv("data-sets/data-sets-uncompressed/data-sets-compressed/Running Imbalance and Speed/Incident Report FB IDs.csv")
Catapult_Session_clean <- Catapult_Session %>%
  #putting the date as a date class
  mutate(Date = as.Date(Date, "%m/%d/%Y")) %>%
  #only selecting important columns for this analysis
  select(anon_id, Date, Age, Primary.Position, Total.Distance, Period.Name, Total.Duration..min., Velocity.Band.1.Total.Distance, Velocity.Band.2.Total.Distance, Velocity.Band.3.Total.Distance, Velocity.Band.4.Total.Distance, Velocity.Band.5.Total.Distance, Velocity.Band.6.Total.Distance, Velocity.Band.7.Total.Distance, Velocity.Band.8.Total.Distance, Maximum.Velocity, Average.Velocity, Hit.90.Percent.Max, Date.of.Last.90.Effort, Days.Since.Last.90.Effort) %>%
  #calculating each player's maximum velocity
  group_by(anon_id) %>%
  mutate(Player.Max.Velocity = max(na.omit(Maximum.Velocity))) %>%
  ungroup() %>%
  #only selecting data from January 1, 2024 and on
  filter(Date >= "2024-01-01")

head(Catapult_Session_clean)
Historical_Running_clean <- Historical_Running %>%
  #taking out rows that don't have data
  filter(Running.Imbalance != "n/a") %>%
  #putting running imbalance as a number and converting the date to a date class
  mutate(Running.Imbalance = as.numeric(Running.Imbalance),
         Date = as.Date(Date, "%m/%d/%Y")) %>%
  #only using data from January 1, 2024 and on
  filter(Date >= "2024-01-01") %>%
  mutate(X=1:4063) %>%
  #making days January 1, 2024
  group_by(anon_id) %>%
  mutate(Days.Since.Start = as.numeric(Date - min(Date))) %>%
  ungroup()

head(Historical_Running_clean)
Incident_Report_clean <- Incident_Report %>%
  #filtering for only hamstring injuries
  filter(OSICS14.Code == "TM1",
         Status != "Full Go")  %>%
  #getting the date of the injury as a date class
  mutate(Date = as.Date(Date, "%m/%d/%Y"),
         Date.of.Injury = as.Date(Date.of.Injury...Onset.of.symptoms, "%m/%d/%Y"),
         Examination.Date = as.Date(Examination.Date, "%m/%d/%Y")) %>%
  #only selecting relevant columns for this analysis
  select(anon_id, Position, Date, Date.of.Injury, Time.of.Injury, Side, OSICS.Injury.Diagnosis, Coach.s.Diagnosis, Recurrence.of.Injury, Choose.Season, Onset.of.Symptoms, Injury.Prognosis, General.Mechanism, Specific.Mechanism, Injured.While., Type.of.Event, Season., Status, Days.in.Status) %>%
  group_by(anon_id, Date.of.Injury) %>%
  mutate(Days.Out = sum(Days.in.Status)) %>%
  ungroup()

head(Incident_Report_clean)
#taking the IDs of players who are and aren't injured
all_IDs <- unique(Historical_Running_clean$anon_id)
#taking IDs that were injured and also have running imbalance data
injured_IDs <- intersect(unique(Incident_Report_clean$anon_id), all_IDs)
#taking all players with running imbalance data that don't have an injury
uninjured_IDs <- unique((Historical_Running_clean %>%
  filter(!anon_id %in% injured_IDs))$anon_id)
#injured players who also have running imbalance data
Incident_Report_clean <- Incident_Report_clean %>%
  filter(anon_id %in% injured_IDs)

#all players that only have running imbalance data or have both running imbalance data and incidence report
Historical_Running_clean <- Historical_Running_clean %>%
  filter(anon_id %in% injured_IDs | anon_id %in% uninjured_IDs)
#removing all_IDs vector and uncleaned data sets
remove(all_IDs)
remove(Incident_Report)
remove(Historical_Running)
remove(Catapult_Session)
Warning in remove(Catapult_Session) :
  object 'Catapult_Session' not found

Section 1: Running Speed

How often are athletes reaching ≥ 90% maximum velocity throughout a training season?

# Bar chart for how often each player reaches ≥ 90% maximum velocity

# Count for how many times each anon_id hit ≥ 90% maximum velocity
hit_90_counts <- Catapult_Session_clean %>%
  filter(Date >= as.Date("2024-06-30") & Date <= as.Date("2025-07-01")) %>%   # Filter for training season
  filter(Hit.90.Percent.Max == "Yes") %>%
  distinct(anon_id,Date) %>%
  group_by(anon_id) %>%
  summarise(times_hit_90 = n())


# Plot of all players' frequencies
ggplot(hit_90_counts, aes(x = anon_id, y = times_hit_90)) +
  geom_bar(stat = "identity", fill = "#CFB87C") +
  geom_hline(yintercept = mean(hit_90_counts$times_hit_90), 
             linetype = "dashed", color = "#565A5C", linewidth = 0.5) +
  labs(title = "Player Counts for Achieving ≥ 90% of Maximum Velocity During 2024–25 Season", 
       subtitle = paste("Team Average:", round(mean(hit_90_counts$times_hit_90), 1)),
       x = "Athlete ID", y = "Times ≥ 90%") +
  theme_classic() +
  theme(axis.text.x = element_text(size = 6, angle = 90))

Delete because we created a function to plot each position

hit_90_counts <- Catapult_Session_clean %>%
  filter(Date >= as.Date("2024-06-30") & Date <= as.Date("2025-07-01"),
         Hit.90.Percent.Max == "Yes") %>%
  distinct(anon_id, Date, .keep_all = TRUE) %>%  # Keep position data
  group_by(anon_id, Primary.Position) %>%        # Group by both ID and position
  summarise(times_hit_90 = n(), .groups = "drop")

QBs <- hit_90_counts %>%
  filter(Primary.Position == "QB")

# Calculate the averages first
overall_avg <- mean(hit_90_counts$times_hit_90)
qb_avg <- mean(QBs$times_hit_90)

ggplot(QBs, aes(x=anon_id, y=times_hit_90)) +
  geom_bar(stat="identity", fill = "#CFB87C") + 
  geom_text(aes(label = times_hit_90), 
            vjust = -0.5, 
            size = 3.5) +
  geom_hline(yintercept = overall_avg, 
             linetype = "dashed", color = "#000000") +
  geom_hline(yintercept = qb_avg, 
             linetype = "dashed", color = "#565A5C") +
  annotate("text", x = 1, y = overall_avg + 0.5, 
           label = "Team Avg", color = "#000000", size = 3) +
  annotate("text", x = 1, y = qb_avg + 0.5, 
           label = "QB Avg", color = "#565A5C", size = 3) +
  labs(title = "QB counts for reaching > 90% max velocity", 
       subtitle = paste("QB Average:", qb_avg)) +
  theme_classic()

Maybe delete facet plot, as it is hard to read

# Facet Plot

# Recalculate hit_90_counts for all positions
hit_90_counts <- Catapult_Session_clean %>%
  filter(Date >= as.Date("2024-06-30") & Date <= as.Date("2025-07-01"),
         Hit.90.Percent.Max == "Yes") %>%
  distinct(anon_id, Date, .keep_all = TRUE) %>%  # Keep position data
  group_by(anon_id, Primary.Position) %>%        # Group by both ID and position
  summarise(times_hit_90 = n(), .groups = "drop")

# Calculate overall average
overall_avg <- mean(hit_90_counts$times_hit_90)

# Plot faceted bar charts by position
ggplot(hit_90_counts, aes(x = anon_id, y = times_hit_90)) +
  geom_bar(stat = "identity", fill = "#CFB87C") +
  geom_text(aes(label = times_hit_90), vjust = -0.5, size = 3.5) +
  geom_hline(yintercept = overall_avg, linetype = "dashed", color = "#000000") +
  annotate("text", x = 1, y = overall_avg + 0.5, 
           label = "Team Avg", color = "#000000", size = 3, hjust = 0) +
  facet_wrap(~ Primary.Position, scales = "free_x") +
  labs(title = "Counts of >90% Max Velocity by Player and Position",
       y = "Times > 90% Max Velocity",
       x = "Player (anon_id)") +
  theme_classic() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))
# Plots for each position

hit_90_counts <- Catapult_Session_clean %>%
  filter(Date >= as.Date("2024-06-30") & Date <= as.Date("2025-07-01"),
         Hit.90.Percent.Max == "Yes") %>%
  distinct(anon_id, Date, .keep_all = TRUE) %>%
  group_by(anon_id, Primary.Position) %>%
  summarise(times_hit_90 = n(), .groups = "drop")

# Get the overall team average once from hit_90_counts
team_avg <- mean(hit_90_counts$times_hit_90)

# Define  positions to loop through
positions <- unique(hit_90_counts$Primary.Position)

# Plotting function:
plot_hit_90_by_position <- function(pos) {
  position_data <- hit_90_counts %>%
    filter(Primary.Position == pos)
  
  pos_avg <- mean(position_data$times_hit_90)
  
  ggplot(position_data, aes(x = anon_id, y = times_hit_90)) +
    geom_bar(stat = "identity", fill = "#CFB87C") +
    geom_text(aes(label = times_hit_90), vjust = -0.5, size = 3.5) +
    geom_hline(yintercept = pos_avg, linetype = "dashed", color = "#565A5C") +
    annotate("text", x = 1, y = pos_avg + 0.5, 
             label = "Pos Avg", color = "#565A5C", size = 3, hjust = 0) +
    geom_hline(yintercept = team_avg, linetype = "dashed", color = "#000000") +
    annotate("text", x = 1, y = team_avg + 0.5, 
             label = "Team Avg", color = "#000000", size = 3, hjust = 0) +
    labs(title = paste("Times ≥90% Max Velocity –", pos),
         subtitle = paste0("Position Avg: ", round(pos_avg, 1),
                           " | Team Avg: ", round(team_avg, 1)),
         y = "Count",
         x = "anon_id") +
    theme_classic() +
    theme(axis.text.x = element_text(angle = 45, hjust = 1))
}

# Loop through each position and print the plot
plots_by_position <- lapply(positions, function(pos) {
  print(plot_hit_90_by_position(pos))
})
# Table for average values of each position

position_averages <- hit_90_counts %>%
  group_by(Primary.Position) %>%
  summarise(avg_times_hit_90 = mean(times_hit_90), .groups = "drop") %>%
  arrange(desc(avg_times_hit_90))

position_averages_with_team <- bind_rows(
  tibble(
    Primary.Position = "Team Average",
    avg_times_hit_90 = team_avg
  ),
  position_averages
)

position_averages_with_team %>%
  gt() %>%
  tab_header(
    title = "Average Times >90% Max Velocity by Position and Team"
  )

Should we consider the number of sprinting efforts that athletes are completing?

# Create weekly sprint count totals

# Create a week variable
Catapult_Session_clean <- Catapult_Session_clean %>%
  mutate(week = floor_date(Date, unit = "week"))

# Get unique athletes and all weeks in your date range
all_athletes <- unique(Catapult_Session_clean$anon_id)
all_weeks <- seq.Date(as.Date("2024-06-30"), as.Date("2025-07-01"), by = "week")

# Create a full grid of anon_id and weeks (this includes weeks with 0 sprints)
full_grid <- expand_grid(anon_id = all_athletes, week = all_weeks)

# Count sprints per athlete-week
sprint_counts <- Catapult_Session_clean %>%
  filter(Hit.90.Percent.Max == "Yes") %>%
  distinct(anon_id, Date, .keep_all = TRUE) %>%
  group_by(anon_id, week) %>%
  summarise(sprint_count = n(), .groups = "drop")

# Join full grid with actual sprint counts and replace NAs with 0s
weekly_sprint_counts <- full_grid %>%
  left_join(sprint_counts, by = c("anon_id", "week")) %>%
  mutate(sprint_count = replace_na(sprint_count, 0))

# Categorize sprint load groups
weekly_sprint_counts <- weekly_sprint_counts %>%
  mutate(sprint_load_group = case_when(
    sprint_count <= 1 ~ "Low (≤1 sprint)",
    sprint_count %in% 2:3 ~ "Moderate (2–3 sprints)",
    sprint_count %in% 4:5 ~ "High (4–5 sprints)"
  ))
# Merge weekly sprints and injury data frames

# Create an injury weeks data frame from incident report
# This should indicate which week the injury occurred
injury_weeks <- Incident_Report_clean %>%
  mutate(
    week = floor_date(as.Date(Date.of.Injury), unit = "week")
  ) %>%
  filter(Date.of.Injury >= as.Date("2024-06-30") & Date.of.Injury <= as.Date("2025-07-01")) %>%
  select(anon_id, week) %>%
  distinct() %>%
  mutate(injury = 1)

# Merge with weekly sprints data frame
weekly_sprint_counts <- weekly_sprint_counts %>%
  left_join(injury_weeks, by = c("anon_id", "week")) %>%
  mutate(injury = ifelse(is.na(injury), 0, injury))  # Fill NAs with 0 (no injury)
# Table to compare injury weeks to non injury weeks
weekly_sprint_counts %>%
  group_by(injury) %>%
  summarise(
    mean_sprints = mean(sprint_count, na.rm = TRUE),
    sd_sprints = sd(sprint_count, na.rm = TRUE),
    n = n()
  )

ggplot(weekly_sprint_counts, aes(x = week, y = sprint_count, color = factor(injury))) +
  geom_line(aes(group = anon_id), alpha = 0.5) +
  geom_point(data = subset(weekly_sprint_counts, injury == 1), size = 2, shape = 21, fill = "red") +
  scale_color_manual(values = c("0" = "grey", "1" = "red"), labels = c("No Injury", "Injury")) +
  labs(title = "Weekly Sprint Counts with Injury Events", x = "Week", y = "Sprint Count", color = "Injury") +
  theme_minimal()

No injuries occurred when an athlete had 4 or 5 sprint counts within a week. There are limited data points in these sprint counts. There are also only three injuries that occur in the moderate range of 2-3 sprint counts The most injuries occur in the low range of 0-1 sprints.

This suggests that underexposure to sprinting may increase injury risk.

# Join to add week number per athlete
weekly_sprint_counts <- weekly_sprint_counts %>%
  group_by(anon_id) %>%
  arrange(week) %>%
  mutate(week_number = row_number()) %>%
  ungroup()

# Identify weeks just before injury
pre_injury_weeks <- weekly_sprint_counts %>%
  group_by(anon_id) %>%
  mutate(
    next_injury = lead(injury),
    is_pre_injury = ifelse(next_injury == 1, 1, 0)
  ) %>%
  ungroup() %>%
  filter(is_pre_injury == 1)

# Compare average sprint counts
pre_injury_weeks %>%
  summarise(mean_pre_injury_sprints = mean(sprint_count, na.rm = TRUE))

weekly_sprint_counts %>%
  filter(injury == 0) %>%
  summarise(mean_non_injury_sprints = mean(sprint_count, na.rm = TRUE))
ggplot(weekly_sprint_counts, aes(x = factor(injury), y = sprint_count, fill = factor(injury))) +
  geom_boxplot() +
  scale_fill_manual(values = c("0" = "lightblue", "1" = "salmon")) +
  labs(title = "Sprint Count Distribution by Injury Status", x = "Injury", y = "Sprint Count") +
  theme_minimal()

Are relative efforts and bands more advantageous than the absolute bands provided?

How does sprinting exposure (# of efforts, % max reached) relate to incidence of hamstring injuries?

Section 2: Running Imbalance

What is the variation at the team level and at each individual athlete level?

Each player tends to have a very unique trend in their running imbalance. Looking at how the team varies but also at how each player varies throughout the season, it’s hard to make out any pattern that’s applicable to most people. The variance of running imbalance varies greatly between each player. Instead we looked at the variances between players who were injured and those who were not. Based on three different bootstrapped findings, we can see that the variances between players who were injured and those who were not were statistically significant. For the first bootstrap, we compared the variance of the pooled groups meaning that the variance in running imbalance for players who were injured and those who were not were compared. This resulted in a 90% confidence interval which suggested that the difference in variance between the two groups is between 0.30 and 3.45. This suggests that when looking at the variance of the two groups separately but all the players are pooled together, the variances will most likely be different by factor between 0.30 and 3.45 and the variance for the injured pool will be greater than that of the uninjured pool. For the second bootstrap, each player’s variance was taken individually. This unpooled approach was taken to see if a player’s variance in running imbalance could potentially be related to HSI risk. The bootstrap algorithm in this case took the averaged variances of the bootstrapped sample for each group and compared them. This bootstrap produced a 90% confidence interval for the difference in average variance between the two groups is 0.79 to 1.32. These results suggest that players who sustained a hamstring injury since January 1, 2024 had, on average, a greater variance in their running imbalance by about 1.06. This suggests that there is a relationship between variance in running imbalance and HSI risk. This found increase in variability will be used to address the following questions. For the third bootstrap, we wanted to see if there was a difference in the average mean absolute value in running imbalance between players who were and weren’t injured. The bootstrapping algorithm for this test calculated the average absolute distance value or each running imbalance measurement and found the average for each sample. This test found that at the 90% significance level, injured players had an average running imbalance absolute value between 0.06 and 0.32 greater than their uninjured counterparts. These values though, when we consider that the range of running imbalance goes from 0 to 100 is small and may be hard to detect when out in the field.

#team variation
Historical_Running_clean %>%
  summarize(Team_Variation = var(Running.Imbalance))

#individual player variation
Historical_Running_clean %>%
  group_by(anon_id) %>%
  summarize(Player_Variation = var(na.omit(Running.Imbalance))) %>%
  ungroup() 
#calculating mean and variance for team data
team_mean <- mean(Historical_Running_clean$Running.Imbalance)
team_sd <- sd(Historical_Running_clean$Running.Imbalance)

#making scatter plot of team running imbalance data throughout season
ggplot(Historical_Running_clean, aes(Date, Running.Imbalance)) +
  geom_point(alpha = 0.3) +
  geom_hline(yintercept = team_mean, color = "#CFB87C") +
  geom_hline(yintercept = team_mean + team_sd) +
  geom_hline(yintercept = team_mean - team_sd) +
  geom_hline(yintercept = team_mean + (2*team_sd), color = "#A2A4A3") +
  geom_hline(yintercept = team_mean - (2*team_sd), color = "#A2A4A3") +
  labs(title = "Team Running Imbalance Since January 1, 2024", y="Running Imbalance (%)",
       subtitle = "\u03BC = 0.08623412, \u03C3^2 = 14.94215")

#making histogram of team running imbalance data
ggplot(Historical_Running_clean, aes(Running.Imbalance)) +
  geom_histogram() +
  labs(title = "Team Running Imbalance Since January 1, 2024", x="Running Imbalance")
#making histogram for running imbalance of all injured athletes
ggplot(data=Historical_Running_clean[Historical_Running_clean$anon_id %in% injured_IDs,], aes(Running.Imbalance)) +
  geom_histogram(fill = "#CFB87C", alpha = 0.75) +
  #adding in 95% confidence interval
  geom_vline(xintercept = quantile(Historical_Running_clean[Historical_Running_clean$anon_id %in% injured_IDs,]$Running.Imbalance, 0.025), color = "#CFB87C") +
  geom_vline(xintercept = quantile(Historical_Running_clean[Historical_Running_clean$anon_id %in% injured_IDs,]$Running.Imbalance, 0.975), color = "#CFB87C") +
  xlim(-21,21) +
  labs(title = "Running Imbalance for Players with HSI since January 1, 2024")
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
Warning: Removed 1 row containing non-finite outside the scale range (`stat_bin()`).

#making histogram for running imbalance of all uninjured athletes
ggplot(data=Historical_Running_clean[Historical_Running_clean$anon_id %in% uninjured_IDs,], aes(Running.Imbalance)) +
  geom_histogram(fill = "black", alpha = 0.75) +
  #adding in 95% confidence interval
  geom_vline(xintercept = quantile(Historical_Running_clean[Historical_Running_clean$anon_id %in% uninjured_IDs,]$Running.Imbalance, 0.025)) +
  geom_vline(xintercept = quantile(Historical_Running_clean[Historical_Running_clean$anon_id %in% uninjured_IDs,]$Running.Imbalance, 0.975)) +
  xlim(-21,21) +
  labs(title = "Running Imbalance for Players without HSI since January 1, 2024")
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
Warning: Removed 1 row containing non-finite outside the scale range (`stat_bin()`).

#Plotting injured and uninjured histograms over top one another
ggplot(data=Historical_Running_clean[Historical_Running_clean$anon_id %in% uninjured_IDs,], aes(Running.Imbalance)) +
  geom_histogram(alpha = 0.75) +
  #adding in 95% CI for uninjured players
  geom_vline(xintercept = quantile(Historical_Running_clean[Historical_Running_clean$anon_id %in% uninjured_IDs,]$Running.Imbalance, 0.025)) +
  geom_vline(xintercept = quantile(Historical_Running_clean[Historical_Running_clean$anon_id %in% uninjured_IDs,]$Running.Imbalance, 0.975)) +
  geom_histogram(data = Historical_Running_clean[Historical_Running_clean$anon_id %in% injured_IDs,], aes(Running.Imbalance), fill = "#CFB87C", alpha = 0.75) +
  #adding in 95% CI for injured players
  geom_vline(xintercept = quantile(Historical_Running_clean[Historical_Running_clean$anon_id %in% injured_IDs,]$Running.Imbalance, 0.025), color = "#CFB87C") +
  geom_vline(xintercept = quantile(Historical_Running_clean[Historical_Running_clean$anon_id %in% injured_IDs,]$Running.Imbalance, 0.975), color = "#CFB87C") +
  xlim(-21,21) +
  labs(title = "Running Imbalance for Players with and without HSI since January 1, 2024")
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
Warning: Removed 1 row containing non-finite outside the scale range (`stat_bin()`).
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
Warning: Removed 1 row containing non-finite outside the scale range (`stat_bin()`).

#making scatter plot of running imbalance data for injured players
ggplot(data=Historical_Running_clean[Historical_Running_clean$anon_id %in% injured_IDs,], aes(Date, Running.Imbalance)) + 
  geom_point(alpha = 0.3) +
  ylim(-21,21) +
  labs(title = "Running Imbalance for Players with HSI since January 1, 2024")
Warning: Removed 1 row containing missing values or values outside the scale range
(`geom_point()`).

#making scatter plot of running imbalance for uninjured players
ggplot(data=Historical_Running_clean[Historical_Running_clean$anon_id %in% uninjured_IDs,], aes(Date, Running.Imbalance)) + 
  geom_point(alpha = 0.3) +
  ylim(-21,21) +
  labs(title = "Running Imbalance for Players without HSI since January 1, 2024")
Warning: Removed 1 row containing missing values or values outside the scale range
(`geom_point()`).

Bootstrapping Differences between Injured and Uninjured Athletes

#Splitting up the data sets and calculating player variance and measurement absolute value
injured_data <- Historical_Running_clean[Historical_Running_clean$anon_id %in% injured_IDs,] %>%
  mutate(Player.Absolute.Dist = abs(Running.Imbalance)) %>%
  group_by(anon_id) %>%
  mutate(Player.Variance = var(Running.Imbalance)) %>%
  ungroup()

uninjured_data <- Historical_Running_clean[Historical_Running_clean$anon_id %in% uninjured_IDs,] %>%
  mutate(Player.Absolute.Dist = abs(Running.Imbalance)) %>%
  group_by(anon_id) %>%
  mutate(Player.Variance = var(Running.Imbalance)) %>%
  ungroup()
#making a data frame to hold all of the within group variances
group_variances <- data.frame(injured_var = rep(NA,1000),
                uninjured_var = rep(NA,1000),
                diff_in_var = rep(NA, 1000))

#bootstrap for variances, 1000 iterations
for(i in 1:1000){
  #random seed
  set.seed(i) 
  
  #taking samples from each of the data sets, same number of rows, replacement true
  injured_sample <- sample_n(injured_data, replace = TRUE, size = 1672)
  uninjured_sample <- sample_n(uninjured_data, replace=TRUE, size = 2391)
  
  #storing the calculated variances in data frame
  group_variances[i,1] = var(injured_sample$Running.Imbalance)
  group_variances[i,2] = var(uninjured_sample$Running.Imbalance)
  group_variances[i,3] = group_variances[i,1] - group_variances[i,2]
}
ggplot(data=group_variances, aes(diff_in_var)) +
  geom_histogram() +
  geom_vline(xintercept = quantile(group_variances$diff_in_var, 0.05), color= "#CFB87C") +
  geom_vline(xintercept = quantile(group_variances$diff_in_var, 0.95), color= "#CFB87C") +
  labs(x="Difference in Variance")
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

ggplot(data=group_variances) +
  geom_histogram(aes(injured_var), alpha = 0.75, fill ="#CFB87C") +
  geom_histogram(aes(uninjured_var), alpha = 0.75) +
  labs(x="Variance")
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

Incident_Report_clean <- Incident_Report_clean %>%
  filter(!is.na(Injury.Prognosis))%>%
  mutate(Expected.Start.Return = as.Date(ifelse(Injury.Prognosis=="No Expected Time Loss",
                                        Date.of.Injury,
                                        ifelse(Injury.Prognosis=="Less than 1 Week",
                                               Date.of.Injury,
                                               ifelse(Injury.Prognosis=="1-4 Weeks",
                                                      Date.of.Injury+days(7),
                                                      Date.of.Injury+days(28))))),
         Expected.End.Return = as.Date(ifelse(Injury.Prognosis=="No Expected Time Loss",
                                        Date.of.Injury,
                                        ifelse(Injury.Prognosis=="Less than 1 Week",
                                               Date.of.Injury+days(7),
                                               ifelse(Injury.Prognosis=="1-4 Weeks",
                                                      Date.of.Injury+days(28),
                                                      Date.of.Injury+days(56)))))) %>%
  group_by(anon_id, Date.of.Injury) %>%
  mutate(Actual.Return = Date.of.Injury+days(sum(Days.in.Status))) %>%
  ungroup()
ggplot(data = mean_player_variances, aes(diff_in_var)) +
  geom_histogram() +
  geom_vline(xintercept = quantile(mean_player_variances$diff_in_var, 0.05), color= "#CFB87C") +
  geom_vline(xintercept = quantile(mean_player_variances$diff_in_var, 0.95), color= "#CFB87C") +
  labs(x="Difference in Variance")
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

ggplot(data=mean_player_variances) +
  geom_histogram(aes(injured_var), alpha = 0.75, fill ="#CFB87C") +
  geom_histogram(aes(uninjured_var), alpha = 0.75) +
  labs(x="Average Variance")
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

#making a data frame to hold all of the average absolute differences from 0
group_distance <- data.frame(injured_dist = rep(NA,1000),
                uninjured_dist = rep(NA,1000),
                diff_in_dist = rep(NA, 1000))

#bootstrap for variances, 1000 iterations
for(i in 1:1000){
  #random seed
  set.seed(i) 
  
  #taking samples from each of the data sets, same number of rows, replacement true
  injured_sample <- sample_n(injured_data, replace = TRUE, size = 1658)
  uninjured_sample <- sample_n(uninjured_data, replace=TRUE, size = 2405)
  
  #storing the calculated variances in data frame
  group_distance[i,1] = mean(injured_sample$Player.Absolute.Dist)
  group_distance[i,2] = mean(uninjured_sample$Player.Absolute.Dist)
  group_distance[i,3] = group_distance[i,1] - group_distance[i,2]
}
ggplot(data = group_distance, aes(diff_in_dist)) +
  geom_histogram() +
  geom_vline(xintercept = quantile(group_distance$diff_in_dist, 0.05), color= "#CFB87C") +
  geom_vline(xintercept = quantile(group_distance$diff_in_dist, 0.95), color= "#CFB87C") +
  labs(x="Difference in Distance")
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

ggplot(data = group_distance) +
  geom_histogram(aes(injured_dist), alpha = 0.75, fill ="#CFB87C") +
  geom_histogram(aes(uninjured_dist), alpha = 0.75) +
  labs(x="Average Distance")
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

#removing junk that came from the loops
remove(i,team_mean, team_sd, injured_sample, uninjured_sample, group_variances, injured_data, uninjured_data, mean_player_variances, group_distance)

What is a meaningful change? What red flags should go off when we see a week-to-week change in running imbalance?

Based on the analysis below, there doesn’t seem to be any major discernible differences in running imbalance before or following an injury. This suggests that there may not be a direct link between HSI risk and running imbalance value directly beforehand. Instead, the trends of many players who were injured seems to have a relatively consistent trend not entirely dependent on time. Looking at summary statistics of running imbalance in the weeks leading up to and following a hamstring injury, there are also no glaring trends. For this analysis, we looked at the mean and variance in running imbalance per week leading up to and after an injury for all of the injured players with running imbalance data. This showed us that there is no clear indicator of HSI risk in running imbalance or any summary statistic of it. Instead it may be more useful to look at each player’s total running imbalance and their individual variance. This seems to be more of a useful tool for differentiating between injured and uninjured athletes.

#getting running imbalances for just injured players
Injured_Historical_Running <- Historical_Running_clean %>%
  filter(anon_id %in% injured_IDs)

#making new column to represent when in time injury would be, negative means before injury and positive means after injury, 0 means date of injury if there's data for that day
Injured_Historical_Running$Weeks.After.Injury <- rep(NA, 1658)
Injured_Historical_Running$Injury.Count <- rep(NA, 1658)

#making new column in incident report for the injury count
Incident_Report_clean$Injury.Count <- rep(NA, 122)

#go through all of the injured players in the data set
for(i in 1:22){
  #get the dates each player was injured
  injury_dates <- unique(Incident_Report_clean[Incident_Report_clean$anon_id==injured_IDs[i],]$Date.of.Injury)
  
  #go through all of the dates in which the player had an injury
  for(j in 1:length(injury_dates)){
    #calculate dates for 1, 2, 3, 4 weeks before and after each injury date
    past_1 <- injury_dates[j]-7
    past_2 <- injury_dates[j]-14
    past_3 <- injury_dates[j]-21
    past_4 <- injury_dates[j]-28
    future_1 <- injury_dates[j]+7
    future_2 <- injury_dates[j]+14
    future_3 <- injury_dates[j]+21
    future_4 <- injury_dates[j]+28
    
    #Calculating how many injuries this is for the player
    injury_count <- as.character((length(injury_dates)) - j + 1)
    
    #compare date of data point for each player to date of injury, store in Weeks.After.Injury column, store injury count
    
    #first week after injury
    Injured_Historical_Running[Injured_Historical_Running$anon_id==injured_IDs[i] &
                                 Injured_Historical_Running$Date > injury_dates[j] & 
                                 Injured_Historical_Running$Date<=future_1,]$Weeks.After.Injury <- "1"
    
    Injured_Historical_Running[Injured_Historical_Running$anon_id==injured_IDs[i] &
                                 Injured_Historical_Running$Date > injury_dates[j] & 
                                 Injured_Historical_Running$Date<=future_1,]$Injury.Count <- injury_count
    
    #second week after injury
    Injured_Historical_Running[Injured_Historical_Running$anon_id==injured_IDs[i] &
                                 Injured_Historical_Running$Date > future_1 & 
                                 Injured_Historical_Running$Date<=future_2,]$Weeks.After.Injury <- "2"
    
    Injured_Historical_Running[Injured_Historical_Running$anon_id==injured_IDs[i] &
                                 Injured_Historical_Running$Date > future_1 & 
                                 Injured_Historical_Running$Date<=future_2,]$Injury.Count <- injury_count
    
    #third week after injury
    Injured_Historical_Running[Injured_Historical_Running$anon_id==injured_IDs[i] &
                                 Injured_Historical_Running$Date > future_2 & 
                                 Injured_Historical_Running$Date<=future_3,]$Weeks.After.Injury <- "3"
    
    Injured_Historical_Running[Injured_Historical_Running$anon_id==injured_IDs[i] &
                                 Injured_Historical_Running$Date > future_2 & 
                                 Injured_Historical_Running$Date<=future_3,]$Injury.Count <- injury_count
    
    #fourth week after injury
    Injured_Historical_Running[Injured_Historical_Running$anon_id==injured_IDs[i] &
                                 Injured_Historical_Running$Date > future_3 & 
                                 Injured_Historical_Running$Date<=future_4,]$Weeks.After.Injury <- "4"
    
    Injured_Historical_Running[Injured_Historical_Running$anon_id==injured_IDs[i] &
                                 Injured_Historical_Running$Date > future_3 & 
                                 Injured_Historical_Running$Date<=future_4,]$Injury.Count <- injury_count    
    #week right before injury
    Injured_Historical_Running[Injured_Historical_Running$anon_id==injured_IDs[i] &
                                 Injured_Historical_Running$Date < injury_dates[j] & 
                                 Injured_Historical_Running$Date>=past_1,]$Weeks.After.Injury <- "-1"
    
    Injured_Historical_Running[Injured_Historical_Running$anon_id==injured_IDs[i] &
                                 Injured_Historical_Running$Date < injury_dates[j] & 
                                 Injured_Historical_Running$Date>=past_1,]$Injury.Count <- injury_count
    #two weeks before injury
    Injured_Historical_Running[Injured_Historical_Running$anon_id==injured_IDs[i] &
                                 Injured_Historical_Running$Date < past_1 & 
                                 Injured_Historical_Running$Date>=past_2,]$Weeks.After.Injury <- "-2"
    
    Injured_Historical_Running[Injured_Historical_Running$anon_id==injured_IDs[i] &
                                 Injured_Historical_Running$Date < past_1 & 
                                 Injured_Historical_Running$Date>=past_2,]$Injury.Count <- injury_count
    
    #three weeks before injury
    Injured_Historical_Running[Injured_Historical_Running$anon_id==injured_IDs[i] &
                                 Injured_Historical_Running$Date < past_2 & 
                                 Injured_Historical_Running$Date>=past_3,]$Weeks.After.Injury <- "-3"
    
    Injured_Historical_Running[Injured_Historical_Running$anon_id==injured_IDs[i] &
                                 Injured_Historical_Running$Date < past_2 & 
                                 Injured_Historical_Running$Date>=past_3,]$Injury.Count <- injury_count
        
    #four weeks before injury
    Injured_Historical_Running[Injured_Historical_Running$anon_id==injured_IDs[i] &
                                 Injured_Historical_Running$Date < past_3 & 
                                 Injured_Historical_Running$Date>=past_4,]$Weeks.After.Injury <- "-4"
    
    Injured_Historical_Running[Injured_Historical_Running$anon_id==injured_IDs[i] &
                                 Injured_Historical_Running$Date < past_3 & 
                                 Injured_Historical_Running$Date>=past_4,]$Injury.Count <- injury_count
    
    #Date of Injury
    Injured_Historical_Running[Injured_Historical_Running$anon_id==injured_IDs[i] &
                                 Injured_Historical_Running$Date == injury_dates[j],]$Weeks.After.Injury <- "0"
    
    Injured_Historical_Running[Injured_Historical_Running$anon_id==injured_IDs[i] &
                                 Injured_Historical_Running$Date == injury_dates[j],]$Injury.Count <- injury_count
    
    
    #adding injury count to indicent report
    Incident_Report_clean[Incident_Report_clean$anon_id==injured_IDs[i] & Incident_Report_clean$Date.of.Injury==injury_dates[j],]$Injury.Count <- injury_count
    
  }
}


#making weeks after injury and injury count into a factor and combining data sets back together
Injured_Historical_Running <- Injured_Historical_Running %>%
  mutate(Weeks.After.Injury = factor(Weeks.After.Injury),
         Injury.Count = factor(Injury.Count))

Historical_Running_clean <- left_join(Historical_Running_clean, Injured_Historical_Running)

#getting rid of junk that was from the loop
remove(future_1, future_2, future_3, future_4, i, injury_count, injury_dates, j, past_1, past_2, past_3, past_4)
remove(Injured_Historical_Running)
for(i in 1:22){
  p <- ggplot(data=Historical_Running_clean[Historical_Running_clean$anon_id==injured_IDs[i],], aes(Date, Running.Imbalance)) +
    geom_line() +
    geom_point(aes(color=Weeks.After.Injury)) +
    geom_vline(xintercept = Incident_Report_clean[Incident_Report_clean$anon_id==injured_IDs[i],]$Date.of.Injury) +
    scale_color_manual(values = c("-4"="green", "-3"="yellow", "-2"="orange", "-1"="red", "0"="black","1"= "purple", "2"="navy", "3"="blue", "4"="skyblue")) +
    theme_minimal() +
    labs(title="Running Imbalance", subtitle = injured_IDs[i])
  
  print(p)
}

#making summary statistics of running imbalance per week relative to injury
Historical_Running_clean <- Historical_Running_clean %>%
  group_by(anon_id, Injury.Count, Weeks.After.Injury) %>%
  mutate(Weeks.After.Injury.Variability = var(Running.Imbalance),
         Weeks.After.Injury.Mean = mean(abs(Running.Imbalance))) %>%
  ungroup()
for(i in 1:22){
  #looking at mean running imbalance per week before and after injury for each injured player
  p <- ggplot(data=Historical_Running_clean[Historical_Running_clean$anon_id == injured_IDs[i],], aes(Date, Weeks.After.Injury.Mean, group=Injury.Count)) +
  geom_line() +
  geom_point(aes(color=Weeks.After.Injury)) +
    xlim(min(Incident_Report_clean[Incident_Report_clean$anon_id==injured_IDs[i],]$Date.of.Injury)-30, max(Incident_Report_clean[Incident_Report_clean$anon_id==injured_IDs[i],]$Date.of.Injury)+30) +
  geom_vline(xintercept = Incident_Report_clean[Incident_Report_clean$anon_id==injured_IDs[i],]$Date.of.Injury) +
    labs(title="Average Absolute Distance per Week",injured_IDs[i]) +
    scale_color_manual(values = c("-4"="green", "-3"="yellow", "-2"="orange", "-1"="red", "0"="black","1"= "purple", "2"="navy", "3"="blue", "4"="skyblue"))
  
  print(p)
}

for(i in 1:22){
  #looking at variance in running imbalance per week before and after injury for each injured player
  p <- ggplot(data=Historical_Running_clean[Historical_Running_clean$anon_id == injured_IDs[i],], aes(Date, Weeks.After.Injury.Variability, group=Injury.Count)) +
  geom_line() +
  geom_point(aes(color=Weeks.After.Injury)) +
    xlim(min(Incident_Report_clean[Incident_Report_clean$anon_id==injured_IDs[i],]$Date.of.Injury)-30, max(Incident_Report_clean[Incident_Report_clean$anon_id==injured_IDs[i],]$Date.of.Injury)+30) +
  geom_vline(xintercept = Incident_Report_clean[Incident_Report_clean$anon_id==injured_IDs[i],]$Date.of.Injury) +
    labs(title="Variance in Running Imbalance per Week", subtitle=injured_IDs[i]) +
    scale_color_manual(values = c("-4"="green", "-3"="yellow", "-2"="orange", "-1"="red", "0"="black","1"= "purple", "2"="navy", "3"="blue", "4"="skyblue"))
  
  print(p)
}
Warning: Removed 18 rows containing missing values or values outside the scale range
(`geom_line()`).
Warning: Removed 20 rows containing missing values or values outside the scale range
(`geom_point()`).
Warning: Removed 16 rows containing missing values or values outside the scale range
(`geom_line()`).
Warning: Removed 17 rows containing missing values or values outside the scale range
(`geom_point()`).
Warning: Removed 93 rows containing missing values or values outside the scale range
(`geom_line()`).
Warning: Removed 97 rows containing missing values or values outside the scale range
(`geom_point()`).
Warning: Removed 71 rows containing missing values or values outside the scale range
(`geom_line()`).
Warning: Removed 73 rows containing missing values or values outside the scale range
(`geom_point()`).
Warning: Removed 17 rows containing missing values or values outside the scale range
(`geom_line()`).
Warning: Removed 18 rows containing missing values or values outside the scale range
(`geom_point()`).
Warning: Removed 16 rows containing missing values or values outside the scale range
(`geom_line()`).
Warning: Removed 16 rows containing missing values or values outside the scale range
(`geom_point()`).
Warning: Removed 17 rows containing missing values or values outside the scale range
(`geom_line()`).
Warning: Removed 19 rows containing missing values or values outside the scale range
(`geom_point()`).
Warning: Removed 16 rows containing missing values or values outside the scale range
(`geom_line()`).
Warning: Removed 16 rows containing missing values or values outside the scale range
(`geom_point()`).
Warning: Removed 25 rows containing missing values or values outside the scale range
(`geom_line()`).
Warning: Removed 25 rows containing missing values or values outside the scale range
(`geom_point()`).
Warning: Removed 25 rows containing missing values or values outside the scale range
(`geom_line()`).
Warning: Removed 25 rows containing missing values or values outside the scale range
(`geom_point()`).
Warning: Removed 30 rows containing missing values or values outside the scale range
(`geom_line()`).
Warning: Removed 31 rows containing missing values or values outside the scale range
(`geom_point()`).
Warning: Removed 52 rows containing missing values or values outside the scale range
(`geom_line()`).
Warning: Removed 55 rows containing missing values or values outside the scale range
(`geom_point()`).
Warning: Removed 27 rows containing missing values or values outside the scale range
(`geom_line()`).
Warning: Removed 27 rows containing missing values or values outside the scale range
(`geom_point()`).
Warning: Removed 100 rows containing missing values or values outside the scale range
(`geom_line()`).
Warning: Removed 101 rows containing missing values or values outside the scale range
(`geom_point()`).
Warning: Removed 102 rows containing missing values or values outside the scale range
(`geom_line()`).
Warning: Removed 104 rows containing missing values or values outside the scale range
(`geom_point()`).
Warning: Removed 48 rows containing missing values or values outside the scale range
(`geom_line()`).
Warning: Removed 49 rows containing missing values or values outside the scale range
(`geom_point()`).
Warning: Removed 134 rows containing missing values or values outside the scale range
(`geom_line()`).
Warning: Removed 135 rows containing missing values or values outside the scale range
(`geom_point()`).
Warning: Removed 46 rows containing missing values or values outside the scale range
(`geom_line()`).
Warning: Removed 46 rows containing missing values or values outside the scale range
(`geom_point()`).
Warning: Removed 28 rows containing missing values or values outside the scale range
(`geom_line()`).
Warning: Removed 28 rows containing missing values or values outside the scale range
(`geom_point()`).
Warning: Removed 113 rows containing missing values or values outside the scale range
(`geom_line()`).
Warning: Removed 113 rows containing missing values or values outside the scale range
(`geom_point()`).
Warning: Removed 131 rows containing missing values or values outside the scale range
(`geom_line()`).
Warning: Removed 131 rows containing missing values or values outside the scale range
(`geom_point()`).
Warning: Removed 27 rows containing missing values or values outside the scale range
(`geom_line()`).
Warning: Removed 27 rows containing missing values or values outside the scale range
(`geom_point()`).

remove(i, p)

Is running imbalance sensitive enough of a metric to use as a prognosis tool versus a rehab tool?

Based on the analysis below, we can see that by solely using variance in running imbalance from the time of the injury to the end of the predicted return-to-play range is not a very strong predictor for whether or not it will take longer for a player to recover or not. In this analysis, we used the running imbalance in the time frame starting with the injury date to the end of the prognosis time frame. With these specific running imbalance values, we calculated the variance in running imbalance and whether or not the athlete returned to play within the predicted time frame or not. This analysis found that when using the variance in these time frames as the only predictor in a logistic regression model, the slope coefficient associated with the variance was statistically significant at the \(\alpha = 0.01\) significance level. With that though, the accuracy of the model was only around 0.6 suggesting that it wasn’t super strong in practice. In order to understand the impact that variance in running imbalance had on whether or not a player returned in the predicted time frame or not, we performed a bootstrap. We separated the observations in which players did and did not return in the predicted time frame into two different data sets. We then sampled from each of these two data sets and calculated the average variance for each sample. This was repeated 1000 times. This gave us an estimate of the average variance in running imbalance for players who returned within the predicted time frame and those who did not. This bootstrap revealed that players who did not return within the predicted time frame had a variance greater by roughly 1.2 during their time of recovery than those who returned on time. This tells us that while variance in running imbalance is not directly strong enough to predict whether or not an athlete will return within the predicted time frame, it can be used to supplement prognosis or make adjustments to the prognosis during the time of recovery of a HSI.

#Calculating date back to play in incident report data set
Incident_Report_clean <- Incident_Report_clean %>%
  filter(!is.na(Injury.Prognosis))%>%
  #calculating how long predicted time loss is based on prognosis
         #beginning of predicted range of return
  mutate(Expected.Start.Return = as.Date(ifelse(Injury.Prognosis=="No Expected Time Loss",
                                        Date.of.Injury,
                                        ifelse(Injury.Prognosis=="Less than 1 Week",
                                               Date.of.Injury,
                                               ifelse(Injury.Prognosis=="1-4 Weeks",
                                                      Date.of.Injury+days(7),
                                                      Date.of.Injury+days(28))))),
         #end of predicted range of return
         Expected.End.Return = as.Date(ifelse(Injury.Prognosis=="No Expected Time Loss",
                                        Date.of.Injury,
                                        ifelse(Injury.Prognosis=="Less than 1 Week",
                                               Date.of.Injury+days(7),
                                               ifelse(Injury.Prognosis=="1-4 Weeks",
                                                      Date.of.Injury+days(28),
                                                      Date.of.Injury+days(56)))))) %>%
  group_by(anon_id, Date.of.Injury) %>%
  #calculating actual date cleared to return
  mutate(Actual.Return = Date.of.Injury+days(sum(na.omit(Days.in.Status)))) %>%
  ungroup()
for(i in 1:22){
  #looking at mean running imbalance per week before and after injury for each injured player
  p <- ggplot(data=Historical_Running_clean[Historical_Running_clean$anon_id == injured_IDs[i],], aes(Date, Running.Imbalance)) +
  geom_line(linewidth=0.1) +
  geom_point() +
    #Marking when injury occurred with red line
  geom_vline(xintercept=Incident_Report_clean[Incident_Report_clean$anon_id==injured_IDs[i],]$Date.of.Injury, color="red") +
    #Marking beginning of predicted return to play range with purple dotted line
  geom_vline(xintercept=Incident_Report_clean[Incident_Report_clean$anon_id==injured_IDs[i],]$Expected.Start.Return, color="purple", linetype=3) +
    #Marking end of predicted return to play range with purple dotted line
  geom_vline(xintercept=Incident_Report_clean[Incident_Report_clean$anon_id==injured_IDs[i],]$Expected.End.Return, color="purple", linetype=3) +
    #Marking actual return date with solid green line
  geom_vline(xintercept=Incident_Report_clean[Incident_Report_clean$anon_id==injured_IDs[i],]$Actual.Return, color="green", linetype=1) +
    labs(title=injured_IDs[i])
  
  print(p)
}

#looking at running imbalance of only injured players
Injured_Historical_Running <- Historical_Running_clean %>%
  filter(anon_id %in% injured_IDs)

#Making binary column if date was in time range of injury prognosis
Injured_Historical_Running$Date.in.Range <- rep(0, 1658)

#go through all of the injured players in the data set
for(i in 1:22){
  #get the dates each player was injured
  injury_dates <- unique(Incident_Report_clean[Incident_Report_clean$anon_id==injured_IDs[i],]$Date.of.Injury)
  
  #go through dates of injury for each player
  for(j in 1:length(injury_dates)){
    if(injured_IDs[i] == "ID_50"){ #ID_50 does not have enough running imbalance data
      break
    }
    #get the expected date of return for that instance of injury
    expected_return <- as.Date(Incident_Report_clean[Incident_Report_clean$anon_id==injured_IDs[i] & Incident_Report_clean$Date.of.Injury==injury_dates[j],]$Expected.End.Return[1])
    
    #if the date in running imbalance is between day of injury and last day of prediction, set as 1
    Injured_Historical_Running[Injured_Historical_Running$anon_id==injured_IDs[i] & Injured_Historical_Running$Date >= injury_dates[j] & Injured_Historical_Running$Date <= expected_return,]$Date.in.Range <- 1
  }
}

#making a column for the variance in running imbalance for each injury instance range
Injured_Historical_Running <- Injured_Historical_Running %>%
  filter(Date.in.Range == 1) %>%
  group_by(anon_id, Injury.Count) %>%
  mutate(Injury.Variability = var(Running.Imbalance)) %>%
  ungroup()

Injured_Data <- left_join(Injured_Historical_Running, Incident_Report_clean, by=c("anon_id", "Injury.Count"), relationship = "many-to-many") %>%
  mutate(Return.in.Range = ifelse(Actual.Return>=Expected.Start.Return & Actual.Return <= Expected.End.Return, 1, 0)) %>%
  filter(!is.na(Injury.Variability),
         !is.na(Return.in.Range))
set.seed(1000)
rows <- sample(1:804, size=598, replace=FALSE)
Injured_train <- Injured_Data[rows,]
Injured_test <- Injured_Data[-rows,]

return_to_play_model <- glm(Return.in.Range~Injury.Variability, data=Injured_train, family="binomial")

summary(return_to_play_model)

Call:
glm(formula = Return.in.Range ~ Injury.Variability, family = "binomial", 
    data = Injured_train)

Coefficients:
                   Estimate Std. Error z value Pr(>|z|)   
(Intercept)        -0.16064    0.12937  -1.242  0.21435   
Injury.Variability -0.03978    0.01514  -2.627  0.00861 **
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

(Dispersion parameter for binomial family taken to be 1)

    Null deviance: 780.05  on 580  degrees of freedom
Residual deviance: 772.99  on 579  degrees of freedom
  (17 observations deleted due to missingness)
AIC: 776.99

Number of Fisher Scoring iterations: 4
Injured_test <- Injured_test %>%
  mutate(Prediction = ifelse(predict(return_to_play_model, newdata=Injured_test, type="response")>0.5, 1, 0))

Injured_test %>%
  summarize(CER = mean(Prediction != Return.in.Range))
NA
return_to_play_cv <- glm(Return.in.Range~Injury.Variability, data=Injured_Data, family="binomial")

cost <- function(obs, pred){
  mean((pred <= 0.5) & obs==1 | (pred > 0.5) & obs==0)
}

set.seed(1000)

ten_cv <- cv.glm(data=Injured_Data,glmfit=return_to_play_cv,cost,K=10)

#extract average error
ten_cv$delta[1]
[1] 0.3923567

Bootstrapping Differences Between In and Out of Range Return to Plays

In_Range <- Injured_Data %>%
  filter(Return.in.Range == 1,
         !is.na(Injury.Variability))

Out_Range <- Injured_Data %>%
  filter(Return.in.Range == 0,
         !is.na(Injury.Variability))
Range_Variances <- data.frame(in.avg.var = rep(NA, 1000),
                              out.avg.var = rep(NA, 1000),
                              diff.avg.var = rep(NA, 1000))

for(i in 1:1000){
  set.seed(i)
  
  in_sample <- sample_n(In_Range, size=308, replace=TRUE)
  out_sample <- sample_n(Out_Range, size=477, replace=TRUE)
  
  Range_Variances[i,1] <- mean(in_sample$Injury.Variability)
  Range_Variances[i,2] <- mean(out_sample$Injury.Variability)
  Range_Variances[i,3] <- Range_Variances[i,2] - Range_Variances[i,1]
}
ggplot(data=Range_Variances, aes(diff.avg.var)) +
  geom_histogram() +
  geom_vline(xintercept = quantile(Range_Variances$diff.avg.var, 0.05), color ="#CFB87C") +
  geom_vline(xintercept = quantile(Range_Variances$diff.avg.var, 0.95), color ="#CFB87C")
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

ggplot(data=Range_Variances) +
  geom_histogram(aes(in.avg.var), alpha = 0.75) +
  geom_histogram(aes(out.avg.var), alpha = 0.75, fill ="#CFB87C") +
  labs(x="Average Variance")
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

remove(p,i,j,rows,ten_cv, cost, Injured_Data, Injured_Historical_Running, Injured_test, Injured_train, Range_Variances, return_to_play_cv, return_to_play_model, expected_return, injury_dates, In_Range, Out_Range, in_sample, out_sample)
LS0tDQp0aXRsZTogIkRhdGEgQW5hbHlzaXMgTm90ZWJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQpgYGB7cn0NCiNBdXRob3JzOiBbSU5TRVJUIE5BTUVTIEhFUkVdDQojQXV0aG9yIERhdGU6IA0KI1B1cnBvc2U6IFRoZSBwdXJwb3NlIG9mIHRoaXMgbm90ZWJvb2sgaXMgdG8gaG91c2UgYWxsIGRhdGEgc2V0IHRyYW5zZm9ybWF0aW9uLCBjbGVhbnNpbmcsIHZpc3VhbGl6YXRpb24sIHN0YXRpc3RpY2FsIGFuYWx5c2lzLCBhbmQgbm90ZS10YWtpbmcgZm9yIHRoZSAyMDI1IENVIEF0aGxldGljIERlcGFydG1lbnQgU3BvcnRzIFNjaWVuY2UgSW50ZXJuc2hpcCBQcm9ncmFtDQoNCiNMQVNUIFVQREFURUQ6IA0KDQojSW5jbHVkaW5nIGhlbHBmdWwgbGlicmFyaWVzDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkocmVhZHhsKQ0KbGlicmFyeShhb2QpDQpsaWJyYXJ5KGd0KQ0KbGlicmFyeShib290KQ0KYGBgDQoNCg0KIyBEYXRhIENsZWFuaW5nDQpgYGB7ciBMb2FkaW5nIGluIHRoZSBkYXRhIHNldHN9DQojbG9hZGluZyBpbiB0aGUgQ2F0YXB1bHQgZGF0YSB0byBsb29rIGF0IHNwcmludGluZyB2YWx1ZXMNCkNhdGFwdWx0X1Nlc3Npb24gPC0gcmVhZF9jc3YoImRhdGEtc2V0cy9kYXRhLXNldHMtdW5jb21wcmVzc2VkL2RhdGEtc2V0cy1jb21wcmVzc2VkL1J1bm5pbmcgSW1iYWxhbmNlIGFuZCBTcGVlZC9DYXRhcHVsdCBTZXNzaW9uIC0gT3V0ZG9vciBGQi5jc3YiKQ0KDQojbG9hZGluZyBpbiB0aGUgSGlzdG9yaWNhbCBSdW5uaW5nIGRhdGEgdG8gbG9vayBhdCBydW5uaW5nIGltYmFsYW5jZSB2YWx1ZXMNCkhpc3RvcmljYWxfUnVubmluZyA8LSByZWFkX2NzdigiZGF0YS1zZXRzL2RhdGEtc2V0cy11bmNvbXByZXNzZWQvZGF0YS1zZXRzLWNvbXByZXNzZWQvUnVubmluZyBJbWJhbGFuY2UgYW5kIFNwZWVkL0NvbXBpbGVkIEhpc3RvcmljYWwgUnVubmluZyBJbWJhbGFuY2UgRkIuY3N2IikNCg0KI2xvYWRpbmcgaW4gdGhlIEluY2lkZW50IFJlcG9ydCB0byBsb29rIGF0IEhTSXMNCkluY2lkZW50X1JlcG9ydCA8LSByZWFkX2NzdigiZGF0YS1zZXRzL2RhdGEtc2V0cy11bmNvbXByZXNzZWQvZGF0YS1zZXRzLWNvbXByZXNzZWQvUnVubmluZyBJbWJhbGFuY2UgYW5kIFNwZWVkL0luY2lkZW50IFJlcG9ydCBGQiBJRHMuY3N2IikNCg0KYGBgDQoNCmBgYHtyIENsZWFuaW5nIENhdGFwdWx0X1Nlc3Npb259DQpDYXRhcHVsdF9TZXNzaW9uX2NsZWFuIDwtIENhdGFwdWx0X1Nlc3Npb24gJT4lDQogICNwdXR0aW5nIHRoZSBkYXRlIGFzIGEgZGF0ZSBjbGFzcw0KICBtdXRhdGUoRGF0ZSA9IGFzLkRhdGUoRGF0ZSwgIiVtLyVkLyVZIikpICU+JQ0KICAjb25seSBzZWxlY3RpbmcgaW1wb3J0YW50IGNvbHVtbnMgZm9yIHRoaXMgYW5hbHlzaXMNCiAgc2VsZWN0KGFub25faWQsIERhdGUsIEFnZSwgUHJpbWFyeS5Qb3NpdGlvbiwgVG90YWwuRGlzdGFuY2UsIFBlcmlvZC5OYW1lLCBUb3RhbC5EdXJhdGlvbi4ubWluLiwgVmVsb2NpdHkuQmFuZC4xLlRvdGFsLkRpc3RhbmNlLCBWZWxvY2l0eS5CYW5kLjIuVG90YWwuRGlzdGFuY2UsIFZlbG9jaXR5LkJhbmQuMy5Ub3RhbC5EaXN0YW5jZSwgVmVsb2NpdHkuQmFuZC40LlRvdGFsLkRpc3RhbmNlLCBWZWxvY2l0eS5CYW5kLjUuVG90YWwuRGlzdGFuY2UsIFZlbG9jaXR5LkJhbmQuNi5Ub3RhbC5EaXN0YW5jZSwgVmVsb2NpdHkuQmFuZC43LlRvdGFsLkRpc3RhbmNlLCBWZWxvY2l0eS5CYW5kLjguVG90YWwuRGlzdGFuY2UsIE1heGltdW0uVmVsb2NpdHksIEF2ZXJhZ2UuVmVsb2NpdHksIEhpdC45MC5QZXJjZW50Lk1heCwgRGF0ZS5vZi5MYXN0LjkwLkVmZm9ydCwgRGF5cy5TaW5jZS5MYXN0LjkwLkVmZm9ydCkgJT4lDQogICNjYWxjdWxhdGluZyBlYWNoIHBsYXllcidzIG1heGltdW0gdmVsb2NpdHkNCiAgZ3JvdXBfYnkoYW5vbl9pZCkgJT4lDQogIG11dGF0ZShQbGF5ZXIuTWF4LlZlbG9jaXR5ID0gbWF4KG5hLm9taXQoTWF4aW11bS5WZWxvY2l0eSkpKSAlPiUNCiAgdW5ncm91cCgpICU+JQ0KICAjb25seSBzZWxlY3RpbmcgZGF0YSBmcm9tIEphbnVhcnkgMSwgMjAyNCBhbmQgb24NCiAgZmlsdGVyKERhdGUgPj0gIjIwMjQtMDEtMDEiKQ0KDQpoZWFkKENhdGFwdWx0X1Nlc3Npb25fY2xlYW4pDQpgYGANCg0KYGBge3IgQ2xlYW5pbmcgSGlzdG9yaWNhbF9SdW5uaW5nfQ0KSGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuIDwtIEhpc3RvcmljYWxfUnVubmluZyAlPiUNCiAgI3Rha2luZyBvdXQgcm93cyB0aGF0IGRvbid0IGhhdmUgZGF0YQ0KICBmaWx0ZXIoUnVubmluZy5JbWJhbGFuY2UgIT0gIm4vYSIpICU+JQ0KICAjcHV0dGluZyBydW5uaW5nIGltYmFsYW5jZSBhcyBhIG51bWJlciBhbmQgY29udmVydGluZyB0aGUgZGF0ZSB0byBhIGRhdGUgY2xhc3MNCiAgbXV0YXRlKFJ1bm5pbmcuSW1iYWxhbmNlID0gYXMubnVtZXJpYyhSdW5uaW5nLkltYmFsYW5jZSksDQogICAgICAgICBEYXRlID0gYXMuRGF0ZShEYXRlLCAiJW0vJWQvJVkiKSkgJT4lDQogICNvbmx5IHVzaW5nIGRhdGEgZnJvbSBKYW51YXJ5IDEsIDIwMjQgYW5kIG9uDQogIGZpbHRlcihEYXRlID49ICIyMDI0LTAxLTAxIikgJT4lDQogIG11dGF0ZShYPTE6NDA2MykgJT4lDQogICNtYWtpbmcgZGF5cyBKYW51YXJ5IDEsIDIwMjQNCiAgZ3JvdXBfYnkoYW5vbl9pZCkgJT4lDQogIG11dGF0ZShEYXlzLlNpbmNlLlN0YXJ0ID0gYXMubnVtZXJpYyhEYXRlIC0gbWluKERhdGUpKSkgJT4lDQogIHVuZ3JvdXAoKQ0KDQpoZWFkKEhpc3RvcmljYWxfUnVubmluZ19jbGVhbikNCmBgYA0KDQpgYGB7ciBDbGVhbmluZyBJbmNpZGVudF9SZXBvcnR9DQpJbmNpZGVudF9SZXBvcnRfY2xlYW4gPC0gSW5jaWRlbnRfUmVwb3J0ICU+JQ0KICAjZmlsdGVyaW5nIGZvciBvbmx5IGhhbXN0cmluZyBpbmp1cmllcw0KICBmaWx0ZXIoT1NJQ1MxNC5Db2RlID09ICJUTTEiLA0KICAgICAgICAgU3RhdHVzICE9ICJGdWxsIEdvIikgICU+JQ0KICAjZ2V0dGluZyB0aGUgZGF0ZSBvZiB0aGUgaW5qdXJ5IGFzIGEgZGF0ZSBjbGFzcw0KICBtdXRhdGUoRGF0ZSA9IGFzLkRhdGUoRGF0ZSwgIiVtLyVkLyVZIiksDQogICAgICAgICBEYXRlLm9mLkluanVyeSA9IGFzLkRhdGUoRGF0ZS5vZi5Jbmp1cnkuLi5PbnNldC5vZi5zeW1wdG9tcywgIiVtLyVkLyVZIiksDQogICAgICAgICBFeGFtaW5hdGlvbi5EYXRlID0gYXMuRGF0ZShFeGFtaW5hdGlvbi5EYXRlLCAiJW0vJWQvJVkiKSkgJT4lDQogICNvbmx5IHNlbGVjdGluZyByZWxldmFudCBjb2x1bW5zIGZvciB0aGlzIGFuYWx5c2lzDQogIHNlbGVjdChhbm9uX2lkLCBQb3NpdGlvbiwgRGF0ZSwgRGF0ZS5vZi5Jbmp1cnksIFRpbWUub2YuSW5qdXJ5LCBTaWRlLCBPU0lDUy5Jbmp1cnkuRGlhZ25vc2lzLCBDb2FjaC5zLkRpYWdub3NpcywgUmVjdXJyZW5jZS5vZi5Jbmp1cnksIENob29zZS5TZWFzb24sIE9uc2V0Lm9mLlN5bXB0b21zLCBJbmp1cnkuUHJvZ25vc2lzLCBHZW5lcmFsLk1lY2hhbmlzbSwgU3BlY2lmaWMuTWVjaGFuaXNtLCBJbmp1cmVkLldoaWxlLiwgVHlwZS5vZi5FdmVudCwgU2Vhc29uLiwgU3RhdHVzLCBEYXlzLmluLlN0YXR1cykgJT4lDQogIGdyb3VwX2J5KGFub25faWQsIERhdGUub2YuSW5qdXJ5KSAlPiUNCiAgbXV0YXRlKERheXMuT3V0ID0gc3VtKERheXMuaW4uU3RhdHVzKSkgJT4lDQogIHVuZ3JvdXAoKQ0KDQpoZWFkKEluY2lkZW50X1JlcG9ydF9jbGVhbikNCmBgYA0KDQpgYGB7ciBJZGVudGlmeWluZyBwbGF5ZXJzIHRoYXQgaGF2ZSBkYXRhIGluIGJvdGggZGF0YSBzZXRzfQ0KI3Rha2luZyB0aGUgSURzIG9mIHBsYXllcnMgd2hvIGFyZSBhbmQgYXJlbid0IGluanVyZWQNCmFsbF9JRHMgPC0gdW5pcXVlKEhpc3RvcmljYWxfUnVubmluZ19jbGVhbiRhbm9uX2lkKQ0KI3Rha2luZyBJRHMgdGhhdCB3ZXJlIGluanVyZWQgYW5kIGFsc28gaGF2ZSBydW5uaW5nIGltYmFsYW5jZSBkYXRhDQppbmp1cmVkX0lEcyA8LSBpbnRlcnNlY3QodW5pcXVlKEluY2lkZW50X1JlcG9ydF9jbGVhbiRhbm9uX2lkKSwgYWxsX0lEcykNCiN0YWtpbmcgYWxsIHBsYXllcnMgd2l0aCBydW5uaW5nIGltYmFsYW5jZSBkYXRhIHRoYXQgZG9uJ3QgaGF2ZSBhbiBpbmp1cnkNCnVuaW5qdXJlZF9JRHMgPC0gdW5pcXVlKChIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW4gJT4lDQogIGZpbHRlcighYW5vbl9pZCAlaW4lIGluanVyZWRfSURzKSkkYW5vbl9pZCkNCmBgYA0KDQpgYGB7ciBGaWx0ZXJpbmcgb3V0IHBsYXllcnMgd2l0aG91dCBwcm9wZXIgZGF0YSBmcm9tIGFsbCBkYXRhIHNldHN9DQojaW5qdXJlZCBwbGF5ZXJzIHdobyBhbHNvIGhhdmUgcnVubmluZyBpbWJhbGFuY2UgZGF0YQ0KSW5jaWRlbnRfUmVwb3J0X2NsZWFuIDwtIEluY2lkZW50X1JlcG9ydF9jbGVhbiAlPiUNCiAgZmlsdGVyKGFub25faWQgJWluJSBpbmp1cmVkX0lEcykNCg0KI2FsbCBwbGF5ZXJzIHRoYXQgb25seSBoYXZlIHJ1bm5pbmcgaW1iYWxhbmNlIGRhdGEgb3IgaGF2ZSBib3RoIHJ1bm5pbmcgaW1iYWxhbmNlIGRhdGEgYW5kIGluY2lkZW5jZSByZXBvcnQNCkhpc3RvcmljYWxfUnVubmluZ19jbGVhbiA8LSBIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW4gJT4lDQogIGZpbHRlcihhbm9uX2lkICVpbiUgaW5qdXJlZF9JRHMgfCBhbm9uX2lkICVpbiUgdW5pbmp1cmVkX0lEcykNCmBgYA0KDQpgYGB7ciBSZW1vdmluZyBhbGwgdW5pbXBvcnRhbnQgb2JqZWN0cyBjcmVhdGVkIGR1cmluZyBjbGVhbmluZ30NCiNyZW1vdmluZyBhbGxfSURzIHZlY3RvciBhbmQgdW5jbGVhbmVkIGRhdGEgc2V0cw0KcmVtb3ZlKGFsbF9JRHMpDQpyZW1vdmUoSW5jaWRlbnRfUmVwb3J0KQ0KcmVtb3ZlKEhpc3RvcmljYWxfUnVubmluZykNCnJlbW92ZShDYXRhcHVsdF9TZXNzaW9uKQ0KYGBgDQoNCiMgU2VjdGlvbiAxOiBSdW5uaW5nIFNwZWVkDQoNCiMjIEhvdyBvZnRlbiBhcmUgYXRobGV0ZXMgcmVhY2hpbmcg4omlIDkwJSBtYXhpbXVtIHZlbG9jaXR5IHRocm91Z2hvdXQgYSB0cmFpbmluZyBzZWFzb24/DQoNCmBgYHtyfQ0KIyBCYXIgY2hhcnQgZm9yIGhvdyBvZnRlbiBlYWNoIHBsYXllciByZWFjaGVzIOKJpSA5MCUgbWF4aW11bSB2ZWxvY2l0eQ0KDQojIENvdW50IGZvciBob3cgbWFueSB0aW1lcyBlYWNoIGFub25faWQgaGl0IOKJpSA5MCUgbWF4aW11bSB2ZWxvY2l0eQ0KaGl0XzkwX2NvdW50cyA8LSBDYXRhcHVsdF9TZXNzaW9uX2NsZWFuICU+JQ0KICBmaWx0ZXIoRGF0ZSA+PSBhcy5EYXRlKCIyMDI0LTA2LTMwIikgJiBEYXRlIDw9IGFzLkRhdGUoIjIwMjUtMDctMDEiKSkgJT4lICAgIyBGaWx0ZXIgZm9yIHRyYWluaW5nIHNlYXNvbg0KICBmaWx0ZXIoSGl0LjkwLlBlcmNlbnQuTWF4ID09ICJZZXMiKSAlPiUNCiAgZGlzdGluY3QoYW5vbl9pZCxEYXRlKSAlPiUNCiAgZ3JvdXBfYnkoYW5vbl9pZCkgJT4lDQogIHN1bW1hcmlzZSh0aW1lc19oaXRfOTAgPSBuKCkpDQoNCg0KIyBQbG90IG9mIGFsbCBwbGF5ZXJzJyBmcmVxdWVuY2llcw0KZ2dwbG90KGhpdF85MF9jb3VudHMsIGFlcyh4ID0gYW5vbl9pZCwgeSA9IHRpbWVzX2hpdF85MCkpICsNCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIGZpbGwgPSAiI0NGQjg3QyIpICsNCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gbWVhbihoaXRfOTBfY291bnRzJHRpbWVzX2hpdF85MCksIA0KICAgICAgICAgICAgIGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gIiM1NjVBNUMiLCBsaW5ld2lkdGggPSAwLjUpICsNCiAgbGFicyh0aXRsZSA9ICJQbGF5ZXIgQ291bnRzIGZvciBBY2hpZXZpbmcg4omlIDkwJSBvZiBNYXhpbXVtIFZlbG9jaXR5IER1cmluZyAyMDI04oCTMjUgU2Vhc29uIiwgDQogICAgICAgc3VidGl0bGUgPSBwYXN0ZSgiVGVhbSBBdmVyYWdlOiIsIHJvdW5kKG1lYW4oaGl0XzkwX2NvdW50cyR0aW1lc19oaXRfOTApLCAxKSksDQogICAgICAgeCA9ICJBdGhsZXRlIElEIiwgeSA9ICJUaW1lcyDiiaUgOTAlIikgKw0KICB0aGVtZV9jbGFzc2ljKCkgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gNiwgYW5nbGUgPSA5MCkpDQpgYGANCiMgRGVsZXRlIGJlY2F1c2Ugd2UgY3JlYXRlZCBhIGZ1bmN0aW9uIHRvIHBsb3QgZWFjaCBwb3NpdGlvbg0KYGBge3J9DQpoaXRfOTBfY291bnRzIDwtIENhdGFwdWx0X1Nlc3Npb25fY2xlYW4gJT4lDQogIGZpbHRlcihEYXRlID49IGFzLkRhdGUoIjIwMjQtMDYtMzAiKSAmIERhdGUgPD0gYXMuRGF0ZSgiMjAyNS0wNy0wMSIpLA0KICAgICAgICAgSGl0LjkwLlBlcmNlbnQuTWF4ID09ICJZZXMiKSAlPiUNCiAgZGlzdGluY3QoYW5vbl9pZCwgRGF0ZSwgLmtlZXBfYWxsID0gVFJVRSkgJT4lICAjIEtlZXAgcG9zaXRpb24gZGF0YQ0KICBncm91cF9ieShhbm9uX2lkLCBQcmltYXJ5LlBvc2l0aW9uKSAlPiUgICAgICAgICMgR3JvdXAgYnkgYm90aCBJRCBhbmQgcG9zaXRpb24NCiAgc3VtbWFyaXNlKHRpbWVzX2hpdF85MCA9IG4oKSwgLmdyb3VwcyA9ICJkcm9wIikNCg0KUUJzIDwtIGhpdF85MF9jb3VudHMgJT4lDQogIGZpbHRlcihQcmltYXJ5LlBvc2l0aW9uID09ICJRQiIpDQoNCiMgQ2FsY3VsYXRlIHRoZSBhdmVyYWdlcyBmaXJzdA0Kb3ZlcmFsbF9hdmcgPC0gbWVhbihoaXRfOTBfY291bnRzJHRpbWVzX2hpdF85MCkNCnFiX2F2ZyA8LSBtZWFuKFFCcyR0aW1lc19oaXRfOTApDQoNCmdncGxvdChRQnMsIGFlcyh4PWFub25faWQsIHk9dGltZXNfaGl0XzkwKSkgKw0KICBnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIsIGZpbGwgPSAiI0NGQjg3QyIpICsgDQogIGdlb21fdGV4dChhZXMobGFiZWwgPSB0aW1lc19oaXRfOTApLCANCiAgICAgICAgICAgIHZqdXN0ID0gLTAuNSwgDQogICAgICAgICAgICBzaXplID0gMy41KSArDQogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IG92ZXJhbGxfYXZnLCANCiAgICAgICAgICAgICBsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvciA9ICIjMDAwMDAwIikgKw0KICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSBxYl9hdmcsIA0KICAgICAgICAgICAgIGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gIiM1NjVBNUMiKSArDQogIGFubm90YXRlKCJ0ZXh0IiwgeCA9IDEsIHkgPSBvdmVyYWxsX2F2ZyArIDAuNSwgDQogICAgICAgICAgIGxhYmVsID0gIlRlYW0gQXZnIiwgY29sb3IgPSAiIzAwMDAwMCIsIHNpemUgPSAzKSArDQogIGFubm90YXRlKCJ0ZXh0IiwgeCA9IDEsIHkgPSBxYl9hdmcgKyAwLjUsIA0KICAgICAgICAgICBsYWJlbCA9ICJRQiBBdmciLCBjb2xvciA9ICIjNTY1QTVDIiwgc2l6ZSA9IDMpICsNCiAgbGFicyh0aXRsZSA9ICJRQiBjb3VudHMgZm9yIHJlYWNoaW5nID4gOTAlIG1heCB2ZWxvY2l0eSIsIA0KICAgICAgIHN1YnRpdGxlID0gcGFzdGUoIlFCIEF2ZXJhZ2U6IiwgcWJfYXZnKSkgKw0KICB0aGVtZV9jbGFzc2ljKCkNCg0KYGBgDQojIyBNYXliZSBkZWxldGUgZmFjZXQgcGxvdCwgYXMgaXQgaXMgaGFyZCB0byByZWFkDQpgYGB7cn0NCiMgRmFjZXQgUGxvdA0KDQojIFJlY2FsY3VsYXRlIGhpdF85MF9jb3VudHMgZm9yIGFsbCBwb3NpdGlvbnMNCmhpdF85MF9jb3VudHMgPC0gQ2F0YXB1bHRfU2Vzc2lvbl9jbGVhbiAlPiUNCiAgZmlsdGVyKERhdGUgPj0gYXMuRGF0ZSgiMjAyNC0wNi0zMCIpICYgRGF0ZSA8PSBhcy5EYXRlKCIyMDI1LTA3LTAxIiksDQogICAgICAgICBIaXQuOTAuUGVyY2VudC5NYXggPT0gIlllcyIpICU+JQ0KICBkaXN0aW5jdChhbm9uX2lkLCBEYXRlLCAua2VlcF9hbGwgPSBUUlVFKSAlPiUgICMgS2VlcCBwb3NpdGlvbiBkYXRhDQogIGdyb3VwX2J5KGFub25faWQsIFByaW1hcnkuUG9zaXRpb24pICU+JSAgICAgICAgIyBHcm91cCBieSBib3RoIElEIGFuZCBwb3NpdGlvbg0KICBzdW1tYXJpc2UodGltZXNfaGl0XzkwID0gbigpLCAuZ3JvdXBzID0gImRyb3AiKQ0KDQojIENhbGN1bGF0ZSBvdmVyYWxsIGF2ZXJhZ2UNCm92ZXJhbGxfYXZnIDwtIG1lYW4oaGl0XzkwX2NvdW50cyR0aW1lc19oaXRfOTApDQoNCiMgUGxvdCBmYWNldGVkIGJhciBjaGFydHMgYnkgcG9zaXRpb24NCmdncGxvdChoaXRfOTBfY291bnRzLCBhZXMoeCA9IGFub25faWQsIHkgPSB0aW1lc19oaXRfOTApKSArDQogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBmaWxsID0gIiNDRkI4N0MiKSArDQogIGdlb21fdGV4dChhZXMobGFiZWwgPSB0aW1lc19oaXRfOTApLCB2anVzdCA9IC0wLjUsIHNpemUgPSAzLjUpICsNCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gb3ZlcmFsbF9hdmcsIGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gIiMwMDAwMDAiKSArDQogIGFubm90YXRlKCJ0ZXh0IiwgeCA9IDEsIHkgPSBvdmVyYWxsX2F2ZyArIDAuNSwgDQogICAgICAgICAgIGxhYmVsID0gIlRlYW0gQXZnIiwgY29sb3IgPSAiIzAwMDAwMCIsIHNpemUgPSAzLCBoanVzdCA9IDApICsNCiAgZmFjZXRfd3JhcCh+IFByaW1hcnkuUG9zaXRpb24sIHNjYWxlcyA9ICJmcmVlX3giKSArDQogIGxhYnModGl0bGUgPSAiQ291bnRzIG9mID45MCUgTWF4IFZlbG9jaXR5IGJ5IFBsYXllciBhbmQgUG9zaXRpb24iLA0KICAgICAgIHkgPSAiVGltZXMgPiA5MCUgTWF4IFZlbG9jaXR5IiwNCiAgICAgICB4ID0gIlBsYXllciAoYW5vbl9pZCkiKSArDQogIHRoZW1lX2NsYXNzaWMoKSArDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSkpDQpgYGANCg0KYGBge3J9DQojIFBsb3RzIGZvciBlYWNoIHBvc2l0aW9uDQoNCmhpdF85MF9jb3VudHMgPC0gQ2F0YXB1bHRfU2Vzc2lvbl9jbGVhbiAlPiUNCiAgZmlsdGVyKERhdGUgPj0gYXMuRGF0ZSgiMjAyNC0wNi0zMCIpICYgRGF0ZSA8PSBhcy5EYXRlKCIyMDI1LTA3LTAxIiksDQogICAgICAgICBIaXQuOTAuUGVyY2VudC5NYXggPT0gIlllcyIpICU+JQ0KICBkaXN0aW5jdChhbm9uX2lkLCBEYXRlLCAua2VlcF9hbGwgPSBUUlVFKSAlPiUNCiAgZ3JvdXBfYnkoYW5vbl9pZCwgUHJpbWFyeS5Qb3NpdGlvbikgJT4lDQogIHN1bW1hcmlzZSh0aW1lc19oaXRfOTAgPSBuKCksIC5ncm91cHMgPSAiZHJvcCIpDQoNCiMgR2V0IHRoZSBvdmVyYWxsIHRlYW0gYXZlcmFnZSBvbmNlIGZyb20gaGl0XzkwX2NvdW50cw0KdGVhbV9hdmcgPC0gbWVhbihoaXRfOTBfY291bnRzJHRpbWVzX2hpdF85MCkNCg0KIyBEZWZpbmUgIHBvc2l0aW9ucyB0byBsb29wIHRocm91Z2gNCnBvc2l0aW9ucyA8LSB1bmlxdWUoaGl0XzkwX2NvdW50cyRQcmltYXJ5LlBvc2l0aW9uKQ0KDQojIFBsb3R0aW5nIGZ1bmN0aW9uOg0KcGxvdF9oaXRfOTBfYnlfcG9zaXRpb24gPC0gZnVuY3Rpb24ocG9zKSB7DQogIHBvc2l0aW9uX2RhdGEgPC0gaGl0XzkwX2NvdW50cyAlPiUNCiAgICBmaWx0ZXIoUHJpbWFyeS5Qb3NpdGlvbiA9PSBwb3MpDQogIA0KICBwb3NfYXZnIDwtIG1lYW4ocG9zaXRpb25fZGF0YSR0aW1lc19oaXRfOTApDQogIA0KICBnZ3Bsb3QocG9zaXRpb25fZGF0YSwgYWVzKHggPSBhbm9uX2lkLCB5ID0gdGltZXNfaGl0XzkwKSkgKw0KICAgIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBmaWxsID0gIiNDRkI4N0MiKSArDQogICAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHRpbWVzX2hpdF85MCksIHZqdXN0ID0gLTAuNSwgc2l6ZSA9IDMuNSkgKw0KICAgIGdlb21faGxpbmUoeWludGVyY2VwdCA9IHBvc19hdmcsIGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gIiM1NjVBNUMiKSArDQogICAgYW5ub3RhdGUoInRleHQiLCB4ID0gMSwgeSA9IHBvc19hdmcgKyAwLjUsIA0KICAgICAgICAgICAgIGxhYmVsID0gIlBvcyBBdmciLCBjb2xvciA9ICIjNTY1QTVDIiwgc2l6ZSA9IDMsIGhqdXN0ID0gMCkgKw0KICAgIGdlb21faGxpbmUoeWludGVyY2VwdCA9IHRlYW1fYXZnLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvciA9ICIjMDAwMDAwIikgKw0KICAgIGFubm90YXRlKCJ0ZXh0IiwgeCA9IDEsIHkgPSB0ZWFtX2F2ZyArIDAuNSwgDQogICAgICAgICAgICAgbGFiZWwgPSAiVGVhbSBBdmciLCBjb2xvciA9ICIjMDAwMDAwIiwgc2l6ZSA9IDMsIGhqdXN0ID0gMCkgKw0KICAgIGxhYnModGl0bGUgPSBwYXN0ZSgiVGltZXMg4omlOTAlIE1heCBWZWxvY2l0eSDigJMiLCBwb3MpLA0KICAgICAgICAgc3VidGl0bGUgPSBwYXN0ZTAoIlBvc2l0aW9uIEF2ZzogIiwgcm91bmQocG9zX2F2ZywgMSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAiIHwgVGVhbSBBdmc6ICIsIHJvdW5kKHRlYW1fYXZnLCAxKSksDQogICAgICAgICB5ID0gIkNvdW50IiwNCiAgICAgICAgIHggPSAiYW5vbl9pZCIpICsNCiAgICB0aGVtZV9jbGFzc2ljKCkgKw0KICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSkpDQp9DQoNCiMgTG9vcCB0aHJvdWdoIGVhY2ggcG9zaXRpb24gYW5kIHByaW50IHRoZSBwbG90DQpwbG90c19ieV9wb3NpdGlvbiA8LSBsYXBwbHkocG9zaXRpb25zLCBmdW5jdGlvbihwb3MpIHsNCiAgcHJpbnQocGxvdF9oaXRfOTBfYnlfcG9zaXRpb24ocG9zKSkNCn0pDQoNCmBgYA0KDQpgYGB7cn0NCiMgVGFibGUgZm9yIGF2ZXJhZ2UgdmFsdWVzIG9mIGVhY2ggcG9zaXRpb24NCg0KcG9zaXRpb25fYXZlcmFnZXMgPC0gaGl0XzkwX2NvdW50cyAlPiUNCiAgZ3JvdXBfYnkoUHJpbWFyeS5Qb3NpdGlvbikgJT4lDQogIHN1bW1hcmlzZShhdmdfdGltZXNfaGl0XzkwID0gbWVhbih0aW1lc19oaXRfOTApLCAuZ3JvdXBzID0gImRyb3AiKSAlPiUNCiAgYXJyYW5nZShkZXNjKGF2Z190aW1lc19oaXRfOTApKQ0KDQpwb3NpdGlvbl9hdmVyYWdlc193aXRoX3RlYW0gPC0gYmluZF9yb3dzKA0KICB0aWJibGUoDQogICAgUHJpbWFyeS5Qb3NpdGlvbiA9ICJUZWFtIEF2ZXJhZ2UiLA0KICAgIGF2Z190aW1lc19oaXRfOTAgPSB0ZWFtX2F2Zw0KICApLA0KICBwb3NpdGlvbl9hdmVyYWdlcw0KKQ0KDQpwb3NpdGlvbl9hdmVyYWdlc193aXRoX3RlYW0gJT4lDQogIGd0KCkgJT4lDQogIHRhYl9oZWFkZXIoDQogICAgdGl0bGUgPSAiQXZlcmFnZSBUaW1lcyA+OTAlIE1heCBWZWxvY2l0eSBieSBQb3NpdGlvbiBhbmQgVGVhbSINCiAgKQ0KYGBgDQoNCiMjIFNob3VsZCB3ZSBjb25zaWRlciB0aGUgbnVtYmVyIG9mIHNwcmludGluZyBlZmZvcnRzIHRoYXQgYXRobGV0ZXMgYXJlIGNvbXBsZXRpbmc/DQoNCmBgYHtyfQ0KIyBDcmVhdGUgd2Vla2x5IHNwcmludCBjb3VudCB0b3RhbHMNCg0KIyBDcmVhdGUgYSB3ZWVrIHZhcmlhYmxlDQpDYXRhcHVsdF9TZXNzaW9uX2NsZWFuIDwtIENhdGFwdWx0X1Nlc3Npb25fY2xlYW4gJT4lDQogIG11dGF0ZSh3ZWVrID0gZmxvb3JfZGF0ZShEYXRlLCB1bml0ID0gIndlZWsiKSkNCg0KIyBHZXQgdW5pcXVlIGF0aGxldGVzIGFuZCBhbGwgd2Vla3MgaW4geW91ciBkYXRlIHJhbmdlDQphbGxfYXRobGV0ZXMgPC0gdW5pcXVlKENhdGFwdWx0X1Nlc3Npb25fY2xlYW4kYW5vbl9pZCkNCmFsbF93ZWVrcyA8LSBzZXEuRGF0ZShhcy5EYXRlKCIyMDI0LTA2LTMwIiksIGFzLkRhdGUoIjIwMjUtMDctMDEiKSwgYnkgPSAid2VlayIpDQoNCiMgQ3JlYXRlIGEgZnVsbCBncmlkIG9mIGFub25faWQgYW5kIHdlZWtzICh0aGlzIGluY2x1ZGVzIHdlZWtzIHdpdGggMCBzcHJpbnRzKQ0KZnVsbF9ncmlkIDwtIGV4cGFuZF9ncmlkKGFub25faWQgPSBhbGxfYXRobGV0ZXMsIHdlZWsgPSBhbGxfd2Vla3MpDQoNCiMgQ291bnQgc3ByaW50cyBwZXIgYXRobGV0ZS13ZWVrDQpzcHJpbnRfY291bnRzIDwtIENhdGFwdWx0X1Nlc3Npb25fY2xlYW4gJT4lDQogIGZpbHRlcihIaXQuOTAuUGVyY2VudC5NYXggPT0gIlllcyIpICU+JQ0KICBkaXN0aW5jdChhbm9uX2lkLCBEYXRlLCAua2VlcF9hbGwgPSBUUlVFKSAlPiUNCiAgZ3JvdXBfYnkoYW5vbl9pZCwgd2VlaykgJT4lDQogIHN1bW1hcmlzZShzcHJpbnRfY291bnQgPSBuKCksIC5ncm91cHMgPSAiZHJvcCIpDQoNCiMgSm9pbiBmdWxsIGdyaWQgd2l0aCBhY3R1YWwgc3ByaW50IGNvdW50cyBhbmQgcmVwbGFjZSBOQXMgd2l0aCAwcw0Kd2Vla2x5X3NwcmludF9jb3VudHMgPC0gZnVsbF9ncmlkICU+JQ0KICBsZWZ0X2pvaW4oc3ByaW50X2NvdW50cywgYnkgPSBjKCJhbm9uX2lkIiwgIndlZWsiKSkgJT4lDQogIG11dGF0ZShzcHJpbnRfY291bnQgPSByZXBsYWNlX25hKHNwcmludF9jb3VudCwgMCkpDQoNCiMgQ2F0ZWdvcml6ZSBzcHJpbnQgbG9hZCBncm91cHMNCndlZWtseV9zcHJpbnRfY291bnRzIDwtIHdlZWtseV9zcHJpbnRfY291bnRzICU+JQ0KICBtdXRhdGUoc3ByaW50X2xvYWRfZ3JvdXAgPSBjYXNlX3doZW4oDQogICAgc3ByaW50X2NvdW50IDw9IDEgfiAiTG93ICjiiaQxIHNwcmludCkiLA0KICAgIHNwcmludF9jb3VudCAlaW4lIDI6MyB+ICJNb2RlcmF0ZSAoMuKAkzMgc3ByaW50cykiLA0KICAgIHNwcmludF9jb3VudCAlaW4lIDQ6NSB+ICJIaWdoICg04oCTNSBzcHJpbnRzKSINCiAgKSkNCmBgYA0KDQpgYGB7cn0NCiMgTWVyZ2Ugd2Vla2x5IHNwcmludHMgYW5kIGluanVyeSBkYXRhIGZyYW1lcw0KDQojIENyZWF0ZSBhbiBpbmp1cnkgd2Vla3MgZGF0YSBmcmFtZSBmcm9tIGluY2lkZW50IHJlcG9ydA0KIyBUaGlzIHNob3VsZCBpbmRpY2F0ZSB3aGljaCB3ZWVrIHRoZSBpbmp1cnkgb2NjdXJyZWQNCmluanVyeV93ZWVrcyA8LSBJbmNpZGVudF9SZXBvcnRfY2xlYW4gJT4lDQogIG11dGF0ZSgNCiAgICB3ZWVrID0gZmxvb3JfZGF0ZShhcy5EYXRlKERhdGUub2YuSW5qdXJ5KSwgdW5pdCA9ICJ3ZWVrIikNCiAgKSAlPiUNCiAgZmlsdGVyKERhdGUub2YuSW5qdXJ5ID49IGFzLkRhdGUoIjIwMjQtMDYtMzAiKSAmIERhdGUub2YuSW5qdXJ5IDw9IGFzLkRhdGUoIjIwMjUtMDctMDEiKSkgJT4lDQogIHNlbGVjdChhbm9uX2lkLCB3ZWVrKSAlPiUNCiAgZGlzdGluY3QoKSAlPiUNCiAgbXV0YXRlKGluanVyeSA9IDEpDQoNCiMgTWVyZ2Ugd2l0aCB3ZWVrbHkgc3ByaW50cyBkYXRhIGZyYW1lDQp3ZWVrbHlfc3ByaW50X2NvdW50cyA8LSB3ZWVrbHlfc3ByaW50X2NvdW50cyAlPiUNCiAgbGVmdF9qb2luKGluanVyeV93ZWVrcywgYnkgPSBjKCJhbm9uX2lkIiwgIndlZWsiKSkgJT4lDQogIG11dGF0ZShpbmp1cnkgPSBpZmVsc2UoaXMubmEoaW5qdXJ5KSwgMCwgaW5qdXJ5KSkgICMgRmlsbCBOQXMgd2l0aCAwIChubyBpbmp1cnkpDQpgYGANCg0KYGBge3J9DQojIFRhYmxlIHRvIGNvbXBhcmUgaW5qdXJ5IHdlZWtzIHRvIG5vbiBpbmp1cnkgd2Vla3MNCndlZWtseV9zcHJpbnRfY291bnRzICU+JQ0KICBncm91cF9ieShpbmp1cnkpICU+JQ0KICBzdW1tYXJpc2UoDQogICAgbWVhbl9zcHJpbnRzID0gbWVhbihzcHJpbnRfY291bnQsIG5hLnJtID0gVFJVRSksDQogICAgc2Rfc3ByaW50cyA9IHNkKHNwcmludF9jb3VudCwgbmEucm0gPSBUUlVFKSwNCiAgICBuID0gbigpDQogICkNCg0KZ2dwbG90KHdlZWtseV9zcHJpbnRfY291bnRzLCBhZXMoeCA9IHdlZWssIHkgPSBzcHJpbnRfY291bnQsIGNvbG9yID0gZmFjdG9yKGluanVyeSkpKSArDQogIGdlb21fbGluZShhZXMoZ3JvdXAgPSBhbm9uX2lkKSwgYWxwaGEgPSAwLjUpICsNCiAgZ2VvbV9wb2ludChkYXRhID0gc3Vic2V0KHdlZWtseV9zcHJpbnRfY291bnRzLCBpbmp1cnkgPT0gMSksIHNpemUgPSAyLCBzaGFwZSA9IDIxLCBmaWxsID0gInJlZCIpICsNCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoIjAiID0gImdyZXkiLCAiMSIgPSAicmVkIiksIGxhYmVscyA9IGMoIk5vIEluanVyeSIsICJJbmp1cnkiKSkgKw0KICBsYWJzKHRpdGxlID0gIldlZWtseSBTcHJpbnQgQ291bnRzIHdpdGggSW5qdXJ5IEV2ZW50cyIsIHggPSAiV2VlayIsIHkgPSAiU3ByaW50IENvdW50IiwgY29sb3IgPSAiSW5qdXJ5IikgKw0KICB0aGVtZV9taW5pbWFsKCkNCg0KYGBgDQpObyBpbmp1cmllcyBvY2N1cnJlZCB3aGVuIGFuIGF0aGxldGUgaGFkIDQgb3IgNSBzcHJpbnQgY291bnRzIHdpdGhpbiBhIHdlZWsuIFRoZXJlIGFyZSBsaW1pdGVkIGRhdGEgcG9pbnRzIGluIHRoZXNlIHNwcmludCBjb3VudHMuDQpUaGVyZSBhcmUgYWxzbyBvbmx5IHRocmVlIGluanVyaWVzIHRoYXQgb2NjdXIgaW4gdGhlIG1vZGVyYXRlIHJhbmdlIG9mIDItMyBzcHJpbnQgY291bnRzDQpUaGUgbW9zdCBpbmp1cmllcyBvY2N1ciBpbiB0aGUgbG93IHJhbmdlIG9mIDAtMSBzcHJpbnRzLg0KDQpUaGlzIHN1Z2dlc3RzIHRoYXQgdW5kZXJleHBvc3VyZSB0byBzcHJpbnRpbmcgbWF5IGluY3JlYXNlIGluanVyeSByaXNrLg0KDQpgYGB7cn0NCiMgSm9pbiB0byBhZGQgd2VlayBudW1iZXIgcGVyIGF0aGxldGUNCndlZWtseV9zcHJpbnRfY291bnRzIDwtIHdlZWtseV9zcHJpbnRfY291bnRzICU+JQ0KICBncm91cF9ieShhbm9uX2lkKSAlPiUNCiAgYXJyYW5nZSh3ZWVrKSAlPiUNCiAgbXV0YXRlKHdlZWtfbnVtYmVyID0gcm93X251bWJlcigpKSAlPiUNCiAgdW5ncm91cCgpDQoNCiMgSWRlbnRpZnkgd2Vla3MganVzdCBiZWZvcmUgaW5qdXJ5DQpwcmVfaW5qdXJ5X3dlZWtzIDwtIHdlZWtseV9zcHJpbnRfY291bnRzICU+JQ0KICBncm91cF9ieShhbm9uX2lkKSAlPiUNCiAgbXV0YXRlKA0KICAgIG5leHRfaW5qdXJ5ID0gbGVhZChpbmp1cnkpLA0KICAgIGlzX3ByZV9pbmp1cnkgPSBpZmVsc2UobmV4dF9pbmp1cnkgPT0gMSwgMSwgMCkNCiAgKSAlPiUNCiAgdW5ncm91cCgpICU+JQ0KICBmaWx0ZXIoaXNfcHJlX2luanVyeSA9PSAxKQ0KDQojIENvbXBhcmUgYXZlcmFnZSBzcHJpbnQgY291bnRzDQpwcmVfaW5qdXJ5X3dlZWtzICU+JQ0KICBzdW1tYXJpc2UobWVhbl9wcmVfaW5qdXJ5X3NwcmludHMgPSBtZWFuKHNwcmludF9jb3VudCwgbmEucm0gPSBUUlVFKSkNCg0Kd2Vla2x5X3NwcmludF9jb3VudHMgJT4lDQogIGZpbHRlcihpbmp1cnkgPT0gMCkgJT4lDQogIHN1bW1hcmlzZShtZWFuX25vbl9pbmp1cnlfc3ByaW50cyA9IG1lYW4oc3ByaW50X2NvdW50LCBuYS5ybSA9IFRSVUUpKQ0KDQpgYGANCmBgYHtyfQ0KZ2dwbG90KHdlZWtseV9zcHJpbnRfY291bnRzLCBhZXMoeCA9IGZhY3Rvcihpbmp1cnkpLCB5ID0gc3ByaW50X2NvdW50LCBmaWxsID0gZmFjdG9yKGluanVyeSkpKSArDQogIGdlb21fYm94cGxvdCgpICsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiMCIgPSAibGlnaHRibHVlIiwgIjEiID0gInNhbG1vbiIpKSArDQogIGxhYnModGl0bGUgPSAiU3ByaW50IENvdW50IERpc3RyaWJ1dGlvbiBieSBJbmp1cnkgU3RhdHVzIiwgeCA9ICJJbmp1cnkiLCB5ID0gIlNwcmludCBDb3VudCIpICsNCiAgdGhlbWVfbWluaW1hbCgpDQoNCg0KYGBgDQojIyBBcmUgcmVsYXRpdmUgZWZmb3J0cyBhbmQgYmFuZHMgbW9yZSBhZHZhbnRhZ2VvdXMgdGhhbiB0aGUgYWJzb2x1dGUgYmFuZHMgcHJvdmlkZWQ/DQoNCiMjIEhvdyBkb2VzIHNwcmludGluZyBleHBvc3VyZSAoIyBvZiBlZmZvcnRzLCAlIG1heCByZWFjaGVkKSByZWxhdGUgdG8gaW5jaWRlbmNlIG9mIGhhbXN0cmluZyBpbmp1cmllcz8NCg0KDQoNCiMgU2VjdGlvbiAyOiBSdW5uaW5nIEltYmFsYW5jZQ0KDQojIyBXaGF0IGlzIHRoZSB2YXJpYXRpb24gYXQgdGhlIHRlYW0gbGV2ZWwgYW5kIGF0IGVhY2ggaW5kaXZpZHVhbCBhdGhsZXRlIGxldmVsPw0KDQogIEVhY2ggcGxheWVyIHRlbmRzIHRvIGhhdmUgYSB2ZXJ5IHVuaXF1ZSB0cmVuZCBpbiB0aGVpciBydW5uaW5nIGltYmFsYW5jZS4gTG9va2luZyBhdCBob3cgdGhlIHRlYW0gdmFyaWVzIGJ1dCBhbHNvIGF0IGhvdyBlYWNoIHBsYXllciB2YXJpZXMgdGhyb3VnaG91dCB0aGUgc2Vhc29uLCBpdCdzIGhhcmQgdG8gbWFrZSBvdXQgYW55IHBhdHRlcm4gdGhhdCdzIGFwcGxpY2FibGUgdG8gbW9zdCBwZW9wbGUuIFRoZSB2YXJpYW5jZSBvZiBydW5uaW5nIGltYmFsYW5jZSB2YXJpZXMgZ3JlYXRseSBiZXR3ZWVuIGVhY2ggcGxheWVyLiBJbnN0ZWFkIHdlIGxvb2tlZCBhdCB0aGUgdmFyaWFuY2VzIGJldHdlZW4gcGxheWVycyB3aG8gd2VyZSBpbmp1cmVkIGFuZCB0aG9zZSB3aG8gd2VyZSBub3QuIEJhc2VkIG9uIHRocmVlIGRpZmZlcmVudCBib290c3RyYXBwZWQgZmluZGluZ3MsIHdlIGNhbiBzZWUgdGhhdCB0aGUgdmFyaWFuY2VzIGJldHdlZW4gcGxheWVycyB3aG8gd2VyZSBpbmp1cmVkIGFuZCB0aG9zZSB3aG8gd2VyZSBub3Qgd2VyZSBzdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50Lg0KICBGb3IgdGhlIGZpcnN0IGJvb3RzdHJhcCwgd2UgY29tcGFyZWQgdGhlIHZhcmlhbmNlIG9mIHRoZSBwb29sZWQgZ3JvdXBzIG1lYW5pbmcgdGhhdCB0aGUgdmFyaWFuY2UgaW4gcnVubmluZyBpbWJhbGFuY2UgZm9yIHBsYXllcnMgd2hvIHdlcmUgaW5qdXJlZCBhbmQgdGhvc2Ugd2hvIHdlcmUgbm90IHdlcmUgY29tcGFyZWQuIFRoaXMgcmVzdWx0ZWQgaW4gYSA5MCUgY29uZmlkZW5jZSBpbnRlcnZhbCB3aGljaCBzdWdnZXN0ZWQgdGhhdCB0aGUgZGlmZmVyZW5jZSBpbiB2YXJpYW5jZSBiZXR3ZWVuIHRoZSB0d28gZ3JvdXBzIGlzIGJldHdlZW4gMC4zMCBhbmQgMy40NS4gVGhpcyBzdWdnZXN0cyB0aGF0IHdoZW4gbG9va2luZyBhdCB0aGUgdmFyaWFuY2Ugb2YgdGhlIHR3byBncm91cHMgc2VwYXJhdGVseSBidXQgYWxsIHRoZSBwbGF5ZXJzIGFyZSBwb29sZWQgdG9nZXRoZXIsIHRoZSB2YXJpYW5jZXMgd2lsbCBtb3N0IGxpa2VseSBiZSBkaWZmZXJlbnQgYnkgZmFjdG9yIGJldHdlZW4gMC4zMCBhbmQgMy40NSBhbmQgdGhlIHZhcmlhbmNlIGZvciB0aGUgaW5qdXJlZCBwb29sIHdpbGwgYmUgZ3JlYXRlciB0aGFuIHRoYXQgb2YgdGhlIHVuaW5qdXJlZCBwb29sLg0KICBGb3IgdGhlIHNlY29uZCBib290c3RyYXAsIGVhY2ggcGxheWVyJ3MgdmFyaWFuY2Ugd2FzIHRha2VuIGluZGl2aWR1YWxseS4gVGhpcyB1bnBvb2xlZCBhcHByb2FjaCB3YXMgdGFrZW4gdG8gc2VlIGlmIGEgcGxheWVyJ3MgdmFyaWFuY2UgaW4gcnVubmluZyBpbWJhbGFuY2UgY291bGQgcG90ZW50aWFsbHkgYmUgcmVsYXRlZCB0byBIU0kgcmlzay4gVGhlIGJvb3RzdHJhcCBhbGdvcml0aG0gaW4gdGhpcyBjYXNlIHRvb2sgdGhlIGF2ZXJhZ2VkIHZhcmlhbmNlcyBvZiB0aGUgYm9vdHN0cmFwcGVkIHNhbXBsZSBmb3IgZWFjaCBncm91cCBhbmQgY29tcGFyZWQgdGhlbS4gVGhpcyBib290c3RyYXAgcHJvZHVjZWQgYSA5MCUgY29uZmlkZW5jZSBpbnRlcnZhbCBmb3IgdGhlIGRpZmZlcmVuY2UgaW4gYXZlcmFnZSB2YXJpYW5jZSBiZXR3ZWVuIHRoZSB0d28gZ3JvdXBzIGlzIDAuNzkgdG8gMS4zMi4gVGhlc2UgcmVzdWx0cyBzdWdnZXN0IHRoYXQgcGxheWVycyB3aG8gc3VzdGFpbmVkIGEgaGFtc3RyaW5nIGluanVyeSBzaW5jZSBKYW51YXJ5IDEsIDIwMjQgaGFkLCBvbiBhdmVyYWdlLCBhIGdyZWF0ZXIgdmFyaWFuY2UgaW4gdGhlaXIgcnVubmluZyBpbWJhbGFuY2UgYnkgYWJvdXQgMS4wNi4gVGhpcyBzdWdnZXN0cyB0aGF0IHRoZXJlIGlzIGEgcmVsYXRpb25zaGlwIGJldHdlZW4gdmFyaWFuY2UgaW4gcnVubmluZyBpbWJhbGFuY2UgYW5kIEhTSSByaXNrLiBUaGlzIGZvdW5kIGluY3JlYXNlIGluIHZhcmlhYmlsaXR5IHdpbGwgYmUgdXNlZCB0byBhZGRyZXNzIHRoZSBmb2xsb3dpbmcgcXVlc3Rpb25zLg0KICBGb3IgdGhlIHRoaXJkIGJvb3RzdHJhcCwgd2Ugd2FudGVkIHRvIHNlZSBpZiB0aGVyZSB3YXMgYSBkaWZmZXJlbmNlIGluIHRoZSBhdmVyYWdlIG1lYW4gYWJzb2x1dGUgdmFsdWUgaW4gcnVubmluZyBpbWJhbGFuY2UgYmV0d2VlbiBwbGF5ZXJzIHdobyB3ZXJlIGFuZCB3ZXJlbid0IGluanVyZWQuIFRoZSBib290c3RyYXBwaW5nIGFsZ29yaXRobSBmb3IgdGhpcyB0ZXN0IGNhbGN1bGF0ZWQgdGhlIGF2ZXJhZ2UgYWJzb2x1dGUgZGlzdGFuY2UgdmFsdWUgb3IgZWFjaCBydW5uaW5nIGltYmFsYW5jZSBtZWFzdXJlbWVudCBhbmQgZm91bmQgdGhlIGF2ZXJhZ2UgZm9yIGVhY2ggc2FtcGxlLiBUaGlzIHRlc3QgZm91bmQgdGhhdCBhdCB0aGUgOTAlIHNpZ25pZmljYW5jZSBsZXZlbCwgaW5qdXJlZCBwbGF5ZXJzIGhhZCBhbiBhdmVyYWdlIHJ1bm5pbmcgaW1iYWxhbmNlIGFic29sdXRlIHZhbHVlIGJldHdlZW4gMC4wNiBhbmQgMC4zMiBncmVhdGVyIHRoYW4gdGhlaXIgdW5pbmp1cmVkIGNvdW50ZXJwYXJ0cy4gVGhlc2UgdmFsdWVzIHRob3VnaCwgd2hlbiB3ZSBjb25zaWRlciB0aGF0IHRoZSByYW5nZSBvZiBydW5uaW5nIGltYmFsYW5jZSBnb2VzIGZyb20gMCB0byAxMDAgaXMgc21hbGwgYW5kIG1heSBiZSBoYXJkIHRvIGRldGVjdCB3aGVuIG91dCBpbiB0aGUgZmllbGQuIA0KDQpgYGB7ciBMb29raW5nIGF0IHRlYW0gYW5kIHBsYXllciB2YXJpYW5jZXMgb2YgcnVubmluZyBpbWJhbGFuY2V9DQojdGVhbSB2YXJpYXRpb24NCkhpc3RvcmljYWxfUnVubmluZ19jbGVhbiAlPiUNCiAgc3VtbWFyaXplKFRlYW1fVmFyaWF0aW9uID0gdmFyKFJ1bm5pbmcuSW1iYWxhbmNlKSkNCg0KI2luZGl2aWR1YWwgcGxheWVyIHZhcmlhdGlvbg0KSGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuICU+JQ0KICBncm91cF9ieShhbm9uX2lkKSAlPiUNCiAgc3VtbWFyaXplKFBsYXllcl9WYXJpYXRpb24gPSB2YXIobmEub21pdChSdW5uaW5nLkltYmFsYW5jZSkpKSAlPiUNCiAgdW5ncm91cCgpIA0KYGBgDQoNCg0KYGBge3IgUnVubmluZyBpbWJhbGFuY2UgbWVhc3VyZW1lbnRzIGZvciB3aG9sZSB0ZWFtIHNpbmNlIEphbnVhcnkgMSwgMjAyNH0NCiNjYWxjdWxhdGluZyBtZWFuIGFuZCB2YXJpYW5jZSBmb3IgdGVhbSBkYXRhDQp0ZWFtX21lYW4gPC0gbWVhbihIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW4kUnVubmluZy5JbWJhbGFuY2UpDQp0ZWFtX3NkIDwtIHNkKEhpc3RvcmljYWxfUnVubmluZ19jbGVhbiRSdW5uaW5nLkltYmFsYW5jZSkNCg0KI21ha2luZyBzY2F0dGVyIHBsb3Qgb2YgdGVhbSBydW5uaW5nIGltYmFsYW5jZSBkYXRhIHRocm91Z2hvdXQgc2Vhc29uDQpnZ3Bsb3QoSGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuLCBhZXMoRGF0ZSwgUnVubmluZy5JbWJhbGFuY2UpKSArDQogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjMpICsNCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gdGVhbV9tZWFuLCBjb2xvciA9ICIjQ0ZCODdDIikgKw0KICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSB0ZWFtX21lYW4gKyB0ZWFtX3NkKSArDQogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IHRlYW1fbWVhbiAtIHRlYW1fc2QpICsNCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gdGVhbV9tZWFuICsgKDIqdGVhbV9zZCksIGNvbG9yID0gIiNBMkE0QTMiKSArDQogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IHRlYW1fbWVhbiAtICgyKnRlYW1fc2QpLCBjb2xvciA9ICIjQTJBNEEzIikgKw0KICBsYWJzKHRpdGxlID0gIlRlYW0gUnVubmluZyBJbWJhbGFuY2UgU2luY2UgSmFudWFyeSAxLCAyMDI0IiwgeT0iUnVubmluZyBJbWJhbGFuY2UgKCUpIiwNCiAgICAgICBzdWJ0aXRsZSA9ICJcdTAzQkMgPSAwLjA4NjIzNDEyLCBcdTAzQzNeMiA9IDE0Ljk0MjE1IikNCg0KI21ha2luZyBoaXN0b2dyYW0gb2YgdGVhbSBydW5uaW5nIGltYmFsYW5jZSBkYXRhDQpnZ3Bsb3QoSGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuLCBhZXMoUnVubmluZy5JbWJhbGFuY2UpKSArDQogIGdlb21faGlzdG9ncmFtKCkgKw0KICBsYWJzKHRpdGxlID0gIlRlYW0gUnVubmluZyBJbWJhbGFuY2UgU2luY2UgSmFudWFyeSAxLCAyMDI0IiwgeD0iUnVubmluZyBJbWJhbGFuY2UiKQ0KYGBgDQoNCg0KYGBge3IgTG9va2luZyBhdCBkaXN0cmlidXRpb25zIGZvciBpbmp1cmVkIGFuZCB1bmluanVyZWQgc2VwYXJhdGVseX0NCiNtYWtpbmcgaGlzdG9ncmFtIGZvciBydW5uaW5nIGltYmFsYW5jZSBvZiBhbGwgaW5qdXJlZCBhdGhsZXRlcw0KZ2dwbG90KGRhdGE9SGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuW0hpc3RvcmljYWxfUnVubmluZ19jbGVhbiRhbm9uX2lkICVpbiUgaW5qdXJlZF9JRHMsXSwgYWVzKFJ1bm5pbmcuSW1iYWxhbmNlKSkgKw0KICBnZW9tX2hpc3RvZ3JhbShmaWxsID0gIiNDRkI4N0MiLCBhbHBoYSA9IDAuNzUpICsNCiAgI2FkZGluZyBpbiA5NSUgY29uZmlkZW5jZSBpbnRlcnZhbA0KICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBxdWFudGlsZShIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW5bSGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuJGFub25faWQgJWluJSBpbmp1cmVkX0lEcyxdJFJ1bm5pbmcuSW1iYWxhbmNlLCAwLjAyNSksIGNvbG9yID0gIiNDRkI4N0MiKSArDQogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IHF1YW50aWxlKEhpc3RvcmljYWxfUnVubmluZ19jbGVhbltIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW4kYW5vbl9pZCAlaW4lIGluanVyZWRfSURzLF0kUnVubmluZy5JbWJhbGFuY2UsIDAuOTc1KSwgY29sb3IgPSAiI0NGQjg3QyIpICsNCiAgeGxpbSgtMjEsMjEpICsNCiAgbGFicyh0aXRsZSA9ICJSdW5uaW5nIEltYmFsYW5jZSBmb3IgUGxheWVycyB3aXRoIEhTSSBzaW5jZSBKYW51YXJ5IDEsIDIwMjQiKQ0KDQojbWFraW5nIGhpc3RvZ3JhbSBmb3IgcnVubmluZyBpbWJhbGFuY2Ugb2YgYWxsIHVuaW5qdXJlZCBhdGhsZXRlcw0KZ2dwbG90KGRhdGE9SGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuW0hpc3RvcmljYWxfUnVubmluZ19jbGVhbiRhbm9uX2lkICVpbiUgdW5pbmp1cmVkX0lEcyxdLCBhZXMoUnVubmluZy5JbWJhbGFuY2UpKSArDQogIGdlb21faGlzdG9ncmFtKGZpbGwgPSAiYmxhY2siLCBhbHBoYSA9IDAuNzUpICsNCiAgI2FkZGluZyBpbiA5NSUgY29uZmlkZW5jZSBpbnRlcnZhbA0KICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBxdWFudGlsZShIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW5bSGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuJGFub25faWQgJWluJSB1bmluanVyZWRfSURzLF0kUnVubmluZy5JbWJhbGFuY2UsIDAuMDI1KSkgKw0KICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBxdWFudGlsZShIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW5bSGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuJGFub25faWQgJWluJSB1bmluanVyZWRfSURzLF0kUnVubmluZy5JbWJhbGFuY2UsIDAuOTc1KSkgKw0KICB4bGltKC0yMSwyMSkgKw0KICBsYWJzKHRpdGxlID0gIlJ1bm5pbmcgSW1iYWxhbmNlIGZvciBQbGF5ZXJzIHdpdGhvdXQgSFNJIHNpbmNlIEphbnVhcnkgMSwgMjAyNCIpDQoNCiNQbG90dGluZyBpbmp1cmVkIGFuZCB1bmluanVyZWQgaGlzdG9ncmFtcyBvdmVyIHRvcCBvbmUgYW5vdGhlcg0KZ2dwbG90KGRhdGE9SGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuW0hpc3RvcmljYWxfUnVubmluZ19jbGVhbiRhbm9uX2lkICVpbiUgdW5pbmp1cmVkX0lEcyxdLCBhZXMoUnVubmluZy5JbWJhbGFuY2UpKSArDQogIGdlb21faGlzdG9ncmFtKGFscGhhID0gMC43NSkgKw0KICAjYWRkaW5nIGluIDk1JSBDSSBmb3IgdW5pbmp1cmVkIHBsYXllcnMNCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gcXVhbnRpbGUoSGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuW0hpc3RvcmljYWxfUnVubmluZ19jbGVhbiRhbm9uX2lkICVpbiUgdW5pbmp1cmVkX0lEcyxdJFJ1bm5pbmcuSW1iYWxhbmNlLCAwLjAyNSkpICsNCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gcXVhbnRpbGUoSGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuW0hpc3RvcmljYWxfUnVubmluZ19jbGVhbiRhbm9uX2lkICVpbiUgdW5pbmp1cmVkX0lEcyxdJFJ1bm5pbmcuSW1iYWxhbmNlLCAwLjk3NSkpICsNCiAgZ2VvbV9oaXN0b2dyYW0oZGF0YSA9IEhpc3RvcmljYWxfUnVubmluZ19jbGVhbltIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW4kYW5vbl9pZCAlaW4lIGluanVyZWRfSURzLF0sIGFlcyhSdW5uaW5nLkltYmFsYW5jZSksIGZpbGwgPSAiI0NGQjg3QyIsIGFscGhhID0gMC43NSkgKw0KICAjYWRkaW5nIGluIDk1JSBDSSBmb3IgaW5qdXJlZCBwbGF5ZXJzDQogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IHF1YW50aWxlKEhpc3RvcmljYWxfUnVubmluZ19jbGVhbltIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW4kYW5vbl9pZCAlaW4lIGluanVyZWRfSURzLF0kUnVubmluZy5JbWJhbGFuY2UsIDAuMDI1KSwgY29sb3IgPSAiI0NGQjg3QyIpICsNCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gcXVhbnRpbGUoSGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuW0hpc3RvcmljYWxfUnVubmluZ19jbGVhbiRhbm9uX2lkICVpbiUgaW5qdXJlZF9JRHMsXSRSdW5uaW5nLkltYmFsYW5jZSwgMC45NzUpLCBjb2xvciA9ICIjQ0ZCODdDIikgKw0KICB4bGltKC0yMSwyMSkgKw0KICBsYWJzKHRpdGxlID0gIlJ1bm5pbmcgSW1iYWxhbmNlIGZvciBQbGF5ZXJzIHdpdGggYW5kIHdpdGhvdXQgSFNJIHNpbmNlIEphbnVhcnkgMSwgMjAyNCIpDQpgYGANCg0KDQpgYGB7ciBsb29raW5nIGF0IHRyZW5kcyBmb3IgaW5qdXJlZCBhbmQgdW5pbmp1cmVkIHBsYXllcnMgc2luY2UgMS0xLTIwMjR9DQojbWFraW5nIHNjYXR0ZXIgcGxvdCBvZiBydW5uaW5nIGltYmFsYW5jZSBkYXRhIGZvciBpbmp1cmVkIHBsYXllcnMNCmdncGxvdChkYXRhPUhpc3RvcmljYWxfUnVubmluZ19jbGVhbltIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW4kYW5vbl9pZCAlaW4lIGluanVyZWRfSURzLF0sIGFlcyhEYXRlLCBSdW5uaW5nLkltYmFsYW5jZSkpICsgDQogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjMpICsNCiAgeWxpbSgtMjEsMjEpICsNCiAgbGFicyh0aXRsZSA9ICJSdW5uaW5nIEltYmFsYW5jZSBmb3IgUGxheWVycyB3aXRoIEhTSSBzaW5jZSBKYW51YXJ5IDEsIDIwMjQiKQ0KDQojbWFraW5nIHNjYXR0ZXIgcGxvdCBvZiBydW5uaW5nIGltYmFsYW5jZSBmb3IgdW5pbmp1cmVkIHBsYXllcnMNCmdncGxvdChkYXRhPUhpc3RvcmljYWxfUnVubmluZ19jbGVhbltIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW4kYW5vbl9pZCAlaW4lIHVuaW5qdXJlZF9JRHMsXSwgYWVzKERhdGUsIFJ1bm5pbmcuSW1iYWxhbmNlKSkgKyANCiAgZ2VvbV9wb2ludChhbHBoYSA9IDAuMykgKw0KICB5bGltKC0yMSwyMSkgKw0KICBsYWJzKHRpdGxlID0gIlJ1bm5pbmcgSW1iYWxhbmNlIGZvciBQbGF5ZXJzIHdpdGhvdXQgSFNJIHNpbmNlIEphbnVhcnkgMSwgMjAyNCIpDQpgYGANCg0KDQojIyMgQm9vdHN0cmFwcGluZyBEaWZmZXJlbmNlcyBiZXR3ZWVuIEluanVyZWQgYW5kIFVuaW5qdXJlZCBBdGhsZXRlcw0KYGBge3IgU2VwYXJhdGluZyBpbmp1cmVkIGFuZCB1bmluanVyZWQgaW50byB0d28gZGlmZmVyZW50IGRhdGEgc2V0c30NCiNTcGxpdHRpbmcgdXAgdGhlIGRhdGEgc2V0cyBhbmQgY2FsY3VsYXRpbmcgcGxheWVyIHZhcmlhbmNlIGFuZCBtZWFzdXJlbWVudCBhYnNvbHV0ZSB2YWx1ZQ0KaW5qdXJlZF9kYXRhIDwtIEhpc3RvcmljYWxfUnVubmluZ19jbGVhbltIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW4kYW5vbl9pZCAlaW4lIGluanVyZWRfSURzLF0gJT4lDQogIG11dGF0ZShQbGF5ZXIuQWJzb2x1dGUuRGlzdCA9IGFicyhSdW5uaW5nLkltYmFsYW5jZSkpICU+JQ0KICBncm91cF9ieShhbm9uX2lkKSAlPiUNCiAgbXV0YXRlKFBsYXllci5WYXJpYW5jZSA9IHZhcihSdW5uaW5nLkltYmFsYW5jZSkpICU+JQ0KICB1bmdyb3VwKCkNCg0KdW5pbmp1cmVkX2RhdGEgPC0gSGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuW0hpc3RvcmljYWxfUnVubmluZ19jbGVhbiRhbm9uX2lkICVpbiUgdW5pbmp1cmVkX0lEcyxdICU+JQ0KICBtdXRhdGUoUGxheWVyLkFic29sdXRlLkRpc3QgPSBhYnMoUnVubmluZy5JbWJhbGFuY2UpKSAlPiUNCiAgZ3JvdXBfYnkoYW5vbl9pZCkgJT4lDQogIG11dGF0ZShQbGF5ZXIuVmFyaWFuY2UgPSB2YXIoUnVubmluZy5JbWJhbGFuY2UpKSAlPiUNCiAgdW5ncm91cCgpDQpgYGANCg0KDQpgYGB7ciBCb29zdHJhcHBpbmcgdG8gbG9vayBhdCB2YXJpYW5jZXMgb2YgZGlmZmVyZW50IGdyb3VwcywgcG9vbGVkfQ0KI21ha2luZyBhIGRhdGEgZnJhbWUgdG8gaG9sZCBhbGwgb2YgdGhlIHdpdGhpbiBncm91cCB2YXJpYW5jZXMNCmdyb3VwX3ZhcmlhbmNlcyA8LSBkYXRhLmZyYW1lKGluanVyZWRfdmFyID0gcmVwKE5BLDEwMDApLA0KICAgICAgICAgICAgICAgIHVuaW5qdXJlZF92YXIgPSByZXAoTkEsMTAwMCksDQogICAgICAgICAgICAgICAgZGlmZl9pbl92YXIgPSByZXAoTkEsIDEwMDApKQ0KDQojYm9vdHN0cmFwIGZvciB2YXJpYW5jZXMsIDEwMDAgaXRlcmF0aW9ucw0KZm9yKGkgaW4gMToxMDAwKXsNCiAgI3JhbmRvbSBzZWVkDQogIHNldC5zZWVkKGkpIA0KICANCiAgI3Rha2luZyBzYW1wbGVzIGZyb20gZWFjaCBvZiB0aGUgZGF0YSBzZXRzLCBzYW1lIG51bWJlciBvZiByb3dzLCByZXBsYWNlbWVudCB0cnVlDQogIGluanVyZWRfc2FtcGxlIDwtIHNhbXBsZV9uKGluanVyZWRfZGF0YSwgcmVwbGFjZSA9IFRSVUUsIHNpemUgPSAxNjcyKQ0KICB1bmluanVyZWRfc2FtcGxlIDwtIHNhbXBsZV9uKHVuaW5qdXJlZF9kYXRhLCByZXBsYWNlPVRSVUUsIHNpemUgPSAyMzkxKQ0KICANCiAgI3N0b3JpbmcgdGhlIGNhbGN1bGF0ZWQgdmFyaWFuY2VzIGluIGRhdGEgZnJhbWUNCiAgZ3JvdXBfdmFyaWFuY2VzW2ksMV0gPSB2YXIoaW5qdXJlZF9zYW1wbGUkUnVubmluZy5JbWJhbGFuY2UpDQogIGdyb3VwX3ZhcmlhbmNlc1tpLDJdID0gdmFyKHVuaW5qdXJlZF9zYW1wbGUkUnVubmluZy5JbWJhbGFuY2UpDQogIGdyb3VwX3ZhcmlhbmNlc1tpLDNdID0gZ3JvdXBfdmFyaWFuY2VzW2ksMV0gLSBncm91cF92YXJpYW5jZXNbaSwyXQ0KfQ0KYGBgDQoNCg0KYGBge3IgUGxvdHRpbmcgcmVzdWx0cyBmcm9tIHBvb2xlZCBib29zdHJhcHBlZCB2YXJpYW5jZXN9DQpnZ3Bsb3QoZGF0YT1ncm91cF92YXJpYW5jZXMsIGFlcyhkaWZmX2luX3ZhcikpICsNCiAgZ2VvbV9oaXN0b2dyYW0oKSArDQogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IHF1YW50aWxlKGdyb3VwX3ZhcmlhbmNlcyRkaWZmX2luX3ZhciwgMC4wNSksIGNvbG9yPSAiI0NGQjg3QyIpICsNCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gcXVhbnRpbGUoZ3JvdXBfdmFyaWFuY2VzJGRpZmZfaW5fdmFyLCAwLjk1KSwgY29sb3I9ICIjQ0ZCODdDIikgKw0KICBsYWJzKHg9IkRpZmZlcmVuY2UgaW4gVmFyaWFuY2UiKQ0KDQpnZ3Bsb3QoZGF0YT1ncm91cF92YXJpYW5jZXMpICsNCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKGluanVyZWRfdmFyKSwgYWxwaGEgPSAwLjc1LCBmaWxsID0iI0NGQjg3QyIpICsNCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKHVuaW5qdXJlZF92YXIpLCBhbHBoYSA9IDAuNzUpICsNCiAgbGFicyh4PSJWYXJpYW5jZSIpDQpgYGANCg0KDQpgYGB7ciBCb290c3RyYXBwaW5nIHRvIGxvb2sgYXQgYXZlcmFnZSBwbGF5ZXIgdmFyaWFuY2VzfQ0KI21ha2luZyBhIGRhdGEgZnJhbWUgdG8gaG9sZCBhbGwgb2YgdGhlIGF2ZXJhZ2UgcGxheWVyIHZhcmlhbmNlcyBiZXR3ZWVuIGdyb3Vwcw0KbWVhbl9wbGF5ZXJfdmFyaWFuY2VzIDwtIGRhdGEuZnJhbWUoaW5qdXJlZF92YXIgPSByZXAoTkEsMTAwMCksDQogICAgICAgICAgICAgICAgdW5pbmp1cmVkX3ZhciA9IHJlcChOQSwxMDAwKSwNCiAgICAgICAgICAgICAgICBkaWZmX2luX3ZhciA9IHJlcChOQSwgMTAwMCkpDQoNCmZvcihpIGluIDE6MTAwMCl7DQogICNyYW5kb20gc2VlZA0KICBzZXQuc2VlZChpKSANCiAgDQogICN0YWtpbmcgc2FtcGxlcyBmcm9tIGVhY2ggb2YgdGhlIGRhdGEgc2V0cywgc2FtZSBudW1iZXIgb2Ygcm93cywgcmVwbGFjZW1lbnQgdHJ1ZQ0KICBpbmp1cmVkX3NhbXBsZSA8LSBzYW1wbGVfbihpbmp1cmVkX2RhdGEsIHJlcGxhY2UgPSBUUlVFLCBzaXplID0gMTY3MikNCiAgdW5pbmp1cmVkX3NhbXBsZSA8LSBzYW1wbGVfbih1bmluanVyZWRfZGF0YSwgcmVwbGFjZT1UUlVFLCBzaXplID0gMjM5MSkNCiAgDQogICNzdG9yaW5nIHRoZSBjYWxjdWxhdGVkIHZhcmlhbmNlcyBpbiBkYXRhIGZyYW1lDQogIG1lYW5fcGxheWVyX3ZhcmlhbmNlc1tpLDFdID0gbWVhbihuYS5vbWl0KGluanVyZWRfc2FtcGxlJFBsYXllci5WYXJpYW5jZSkpDQogIG1lYW5fcGxheWVyX3ZhcmlhbmNlc1tpLDJdID0gbWVhbihuYS5vbWl0KHVuaW5qdXJlZF9zYW1wbGUkUGxheWVyLlZhcmlhbmNlKSkNCiAgbWVhbl9wbGF5ZXJfdmFyaWFuY2VzW2ksM10gPSBtZWFuX3BsYXllcl92YXJpYW5jZXNbaSwxXSAtIG1lYW5fcGxheWVyX3ZhcmlhbmNlc1tpLDJdDQp9DQpgYGANCg0KDQpgYGB7ciBQbG90dGluZyByZXN1bHRzIGZyb20gYXZlcmFnZWQgYm9vdHN0cmFwcGVkIHBsYXllciB2YXJpYW5jZXN9DQpnZ3Bsb3QoZGF0YSA9IG1lYW5fcGxheWVyX3ZhcmlhbmNlcywgYWVzKGRpZmZfaW5fdmFyKSkgKw0KICBnZW9tX2hpc3RvZ3JhbSgpICsNCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gcXVhbnRpbGUobWVhbl9wbGF5ZXJfdmFyaWFuY2VzJGRpZmZfaW5fdmFyLCAwLjA1KSwgY29sb3I9ICIjQ0ZCODdDIikgKw0KICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBxdWFudGlsZShtZWFuX3BsYXllcl92YXJpYW5jZXMkZGlmZl9pbl92YXIsIDAuOTUpLCBjb2xvcj0gIiNDRkI4N0MiKSArDQogIGxhYnMoeD0iRGlmZmVyZW5jZSBpbiBWYXJpYW5jZSIpDQoNCmdncGxvdChkYXRhPW1lYW5fcGxheWVyX3ZhcmlhbmNlcykgKw0KICBnZW9tX2hpc3RvZ3JhbShhZXMoaW5qdXJlZF92YXIpLCBhbHBoYSA9IDAuNzUsIGZpbGwgPSIjQ0ZCODdDIikgKw0KICBnZW9tX2hpc3RvZ3JhbShhZXModW5pbmp1cmVkX3ZhciksIGFscGhhID0gMC43NSkgKw0KICBsYWJzKHg9IkF2ZXJhZ2UgVmFyaWFuY2UiKQ0KYGBgDQoNCg0KYGBge3IgQm9vdHN0cmFwcGluZyB0byBsb29rIGF0IGF2ZXJhZ2UgYWJzb2x1dGUgdmFsdWUgcnVubmluZyBpbWJhbGFuY2V9DQojbWFraW5nIGEgZGF0YSBmcmFtZSB0byBob2xkIGFsbCBvZiB0aGUgYXZlcmFnZSBhYnNvbHV0ZSBkaWZmZXJlbmNlcyBmcm9tIDANCmdyb3VwX2Rpc3RhbmNlIDwtIGRhdGEuZnJhbWUoaW5qdXJlZF9kaXN0ID0gcmVwKE5BLDEwMDApLA0KICAgICAgICAgICAgICAgIHVuaW5qdXJlZF9kaXN0ID0gcmVwKE5BLDEwMDApLA0KICAgICAgICAgICAgICAgIGRpZmZfaW5fZGlzdCA9IHJlcChOQSwgMTAwMCkpDQoNCiNib290c3RyYXAgZm9yIHZhcmlhbmNlcywgMTAwMCBpdGVyYXRpb25zDQpmb3IoaSBpbiAxOjEwMDApew0KICAjcmFuZG9tIHNlZWQNCiAgc2V0LnNlZWQoaSkgDQogIA0KICAjdGFraW5nIHNhbXBsZXMgZnJvbSBlYWNoIG9mIHRoZSBkYXRhIHNldHMsIHNhbWUgbnVtYmVyIG9mIHJvd3MsIHJlcGxhY2VtZW50IHRydWUNCiAgaW5qdXJlZF9zYW1wbGUgPC0gc2FtcGxlX24oaW5qdXJlZF9kYXRhLCByZXBsYWNlID0gVFJVRSwgc2l6ZSA9IDE2NTgpDQogIHVuaW5qdXJlZF9zYW1wbGUgPC0gc2FtcGxlX24odW5pbmp1cmVkX2RhdGEsIHJlcGxhY2U9VFJVRSwgc2l6ZSA9IDI0MDUpDQogIA0KICAjc3RvcmluZyB0aGUgY2FsY3VsYXRlZCB2YXJpYW5jZXMgaW4gZGF0YSBmcmFtZQ0KICBncm91cF9kaXN0YW5jZVtpLDFdID0gbWVhbihpbmp1cmVkX3NhbXBsZSRQbGF5ZXIuQWJzb2x1dGUuRGlzdCkNCiAgZ3JvdXBfZGlzdGFuY2VbaSwyXSA9IG1lYW4odW5pbmp1cmVkX3NhbXBsZSRQbGF5ZXIuQWJzb2x1dGUuRGlzdCkNCiAgZ3JvdXBfZGlzdGFuY2VbaSwzXSA9IGdyb3VwX2Rpc3RhbmNlW2ksMV0gLSBncm91cF9kaXN0YW5jZVtpLDJdDQp9DQpgYGANCg0KDQpgYGB7ciBQbG90dGluZyByZXN1bHRzIGZyb20gYXZlcmFnZWQgYm9vc3RyYXBwZWQgYWJzb2x1dGUgdmFsdWUgcnVubmluZyBpbWJhbGFuY2V9DQpnZ3Bsb3QoZGF0YSA9IGdyb3VwX2Rpc3RhbmNlLCBhZXMoZGlmZl9pbl9kaXN0KSkgKw0KICBnZW9tX2hpc3RvZ3JhbSgpICsNCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gcXVhbnRpbGUoZ3JvdXBfZGlzdGFuY2UkZGlmZl9pbl9kaXN0LCAwLjA1KSwgY29sb3I9ICIjQ0ZCODdDIikgKw0KICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBxdWFudGlsZShncm91cF9kaXN0YW5jZSRkaWZmX2luX2Rpc3QsIDAuOTUpLCBjb2xvcj0gIiNDRkI4N0MiKSArDQogIGxhYnMoeD0iRGlmZmVyZW5jZSBpbiBEaXN0YW5jZSIpDQoNCmdncGxvdChkYXRhID0gZ3JvdXBfZGlzdGFuY2UpICsNCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKGluanVyZWRfZGlzdCksIGFscGhhID0gMC43NSwgZmlsbCA9IiNDRkI4N0MiKSArDQogIGdlb21faGlzdG9ncmFtKGFlcyh1bmluanVyZWRfZGlzdCksIGFscGhhID0gMC43NSkgKw0KICBsYWJzKHg9IkF2ZXJhZ2UgQWJzb2x1dGUgVmFsdWUiKQ0KYGBgDQoNCg0KYGBge3IgUmVtb3ZpbmcgdW5uZWNlc3Nhcnkgb2JqZWN0cyBmcm9tIHJ1bm5pbmcgaW1iYWxhbmNlIHZhcmlhbmNlIGFuYWx5c2lzfQ0KI3JlbW92aW5nIGp1bmsgdGhhdCBjYW1lIGZyb20gdGhlIGxvb3BzDQpyZW1vdmUoaSx0ZWFtX21lYW4sIHRlYW1fc2QsIGluanVyZWRfc2FtcGxlLCB1bmluanVyZWRfc2FtcGxlLCBncm91cF92YXJpYW5jZXMsIGluanVyZWRfZGF0YSwgdW5pbmp1cmVkX2RhdGEsIG1lYW5fcGxheWVyX3ZhcmlhbmNlcywgZ3JvdXBfZGlzdGFuY2UpDQpgYGANCg0KDQojIyBXaGF0IGlzIGEgbWVhbmluZ2Z1bCBjaGFuZ2U/IFdoYXQgcmVkIGZsYWdzIHNob3VsZCBnbyBvZmYgd2hlbiB3ZSBzZWUgYSB3ZWVrLXRvLXdlZWsgY2hhbmdlIGluIHJ1bm5pbmcgaW1iYWxhbmNlPw0KDQogIEJhc2VkIG9uIHRoZSBhbmFseXNpcyBiZWxvdywgdGhlcmUgZG9lc24ndCBzZWVtIHRvIGJlIGFueSBtYWpvciBkaXNjZXJuaWJsZSBkaWZmZXJlbmNlcyBpbiBydW5uaW5nIGltYmFsYW5jZSBiZWZvcmUgb3IgZm9sbG93aW5nIGFuIGluanVyeS4gVGhpcyBzdWdnZXN0cyB0aGF0IHRoZXJlIG1heSBub3QgYmUgYSBkaXJlY3QgbGluayBiZXR3ZWVuIEhTSSByaXNrIGFuZCBydW5uaW5nIGltYmFsYW5jZSB2YWx1ZSBkaXJlY3RseSBiZWZvcmVoYW5kLiBJbnN0ZWFkLCB0aGUgdHJlbmRzIG9mIG1hbnkgcGxheWVycyB3aG8gd2VyZSBpbmp1cmVkIHNlZW1zIHRvIGhhdmUgYSByZWxhdGl2ZWx5IGNvbnNpc3RlbnQgdHJlbmQgbm90IGVudGlyZWx5IGRlcGVuZGVudCBvbiB0aW1lLiANCiAgTG9va2luZyBhdCBzdW1tYXJ5IHN0YXRpc3RpY3Mgb2YgcnVubmluZyBpbWJhbGFuY2UgaW4gdGhlIHdlZWtzIGxlYWRpbmcgdXAgdG8gYW5kIGZvbGxvd2luZyBhIGhhbXN0cmluZyBpbmp1cnksIHRoZXJlIGFyZSBhbHNvIG5vIGdsYXJpbmcgdHJlbmRzLiBGb3IgdGhpcyBhbmFseXNpcywgd2UgbG9va2VkIGF0IHRoZSBtZWFuIGFuZCB2YXJpYW5jZSBpbiBydW5uaW5nIGltYmFsYW5jZSBwZXIgd2VlayBsZWFkaW5nIHVwIHRvIGFuZCBhZnRlciBhbiBpbmp1cnkgZm9yIGFsbCBvZiB0aGUgaW5qdXJlZCBwbGF5ZXJzIHdpdGggcnVubmluZyBpbWJhbGFuY2UgZGF0YS4gVGhpcyBzaG93ZWQgdXMgdGhhdCB0aGVyZSBpcyBubyBjbGVhciBpbmRpY2F0b3Igb2YgSFNJIHJpc2sgaW4gcnVubmluZyBpbWJhbGFuY2Ugb3IgYW55IHN1bW1hcnkgc3RhdGlzdGljIG9mIGl0LiBJbnN0ZWFkIGl0IG1heSBiZSBtb3JlIHVzZWZ1bCB0byBsb29rIGF0IGVhY2ggcGxheWVyJ3MgdG90YWwgcnVubmluZyBpbWJhbGFuY2UgYW5kIHRoZWlyIGluZGl2aWR1YWwgdmFyaWFuY2UuIFRoaXMgc2VlbXMgdG8gYmUgbW9yZSBvZiBhIHVzZWZ1bCB0b29sIGZvciBkaWZmZXJlbnRpYXRpbmcgYmV0d2VlbiBpbmp1cmVkIGFuZCB1bmluanVyZWQgYXRobGV0ZXMuDQoNCmBgYHtyIENhbGN1bGF0aW5nIHdlZWtzIGJlZm9yZSBhbmQgYWZ0ZXIgaW5qdXJ5IG9jY3VyYW5jZSBiYXNlZCBvbiBkYXRlLCBob3cgbWFueSBpbmp1cmllcyBwbGF5ZXIgaGFzfQ0KI2dldHRpbmcgcnVubmluZyBpbWJhbGFuY2VzIGZvciBqdXN0IGluanVyZWQgcGxheWVycw0KSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmcgPC0gSGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuICU+JQ0KICBmaWx0ZXIoYW5vbl9pZCAlaW4lIGluanVyZWRfSURzKQ0KDQojbWFraW5nIG5ldyBjb2x1bW4gdG8gcmVwcmVzZW50IHdoZW4gaW4gdGltZSBpbmp1cnkgd291bGQgYmUsIG5lZ2F0aXZlIG1lYW5zIGJlZm9yZSBpbmp1cnkgYW5kIHBvc2l0aXZlIG1lYW5zIGFmdGVyIGluanVyeSwgMCBtZWFucyBkYXRlIG9mIGluanVyeSBpZiB0aGVyZSdzIGRhdGEgZm9yIHRoYXQgZGF5DQpJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyRXZWVrcy5BZnRlci5Jbmp1cnkgPC0gcmVwKE5BLCAxNjU4KQ0KSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmckSW5qdXJ5LkNvdW50IDwtIHJlcChOQSwgMTY1OCkNCg0KI21ha2luZyBuZXcgY29sdW1uIGluIGluY2lkZW50IHJlcG9ydCBmb3IgdGhlIGluanVyeSBjb3VudA0KSW5jaWRlbnRfUmVwb3J0X2NsZWFuJEluanVyeS5Db3VudCA8LSByZXAoTkEsIDEyMikNCg0KI2dvIHRocm91Z2ggYWxsIG9mIHRoZSBpbmp1cmVkIHBsYXllcnMgaW4gdGhlIGRhdGEgc2V0DQpmb3IoaSBpbiAxOjIyKXsNCiAgI2dldCB0aGUgZGF0ZXMgZWFjaCBwbGF5ZXIgd2FzIGluanVyZWQNCiAgaW5qdXJ5X2RhdGVzIDwtIHVuaXF1ZShJbmNpZGVudF9SZXBvcnRfY2xlYW5bSW5jaWRlbnRfUmVwb3J0X2NsZWFuJGFub25faWQ9PWluanVyZWRfSURzW2ldLF0kRGF0ZS5vZi5Jbmp1cnkpDQogIA0KICAjZ28gdGhyb3VnaCBhbGwgb2YgdGhlIGRhdGVzIGluIHdoaWNoIHRoZSBwbGF5ZXIgaGFkIGFuIGluanVyeQ0KICBmb3IoaiBpbiAxOmxlbmd0aChpbmp1cnlfZGF0ZXMpKXsNCiAgICAjY2FsY3VsYXRlIGRhdGVzIGZvciAxLCAyLCAzLCA0IHdlZWtzIGJlZm9yZSBhbmQgYWZ0ZXIgZWFjaCBpbmp1cnkgZGF0ZQ0KICAgIHBhc3RfMSA8LSBpbmp1cnlfZGF0ZXNbal0tNw0KICAgIHBhc3RfMiA8LSBpbmp1cnlfZGF0ZXNbal0tMTQNCiAgICBwYXN0XzMgPC0gaW5qdXJ5X2RhdGVzW2pdLTIxDQogICAgcGFzdF80IDwtIGluanVyeV9kYXRlc1tqXS0yOA0KICAgIGZ1dHVyZV8xIDwtIGluanVyeV9kYXRlc1tqXSs3DQogICAgZnV0dXJlXzIgPC0gaW5qdXJ5X2RhdGVzW2pdKzE0DQogICAgZnV0dXJlXzMgPC0gaW5qdXJ5X2RhdGVzW2pdKzIxDQogICAgZnV0dXJlXzQgPC0gaW5qdXJ5X2RhdGVzW2pdKzI4DQogICAgDQogICAgI0NhbGN1bGF0aW5nIGhvdyBtYW55IGluanVyaWVzIHRoaXMgaXMgZm9yIHRoZSBwbGF5ZXINCiAgICBpbmp1cnlfY291bnQgPC0gYXMuY2hhcmFjdGVyKChsZW5ndGgoaW5qdXJ5X2RhdGVzKSkgLSBqICsgMSkNCiAgICANCiAgICAjY29tcGFyZSBkYXRlIG9mIGRhdGEgcG9pbnQgZm9yIGVhY2ggcGxheWVyIHRvIGRhdGUgb2YgaW5qdXJ5LCBzdG9yZSBpbiBXZWVrcy5BZnRlci5Jbmp1cnkgY29sdW1uLCBzdG9yZSBpbmp1cnkgY291bnQNCiAgICANCiAgICAjZmlyc3Qgd2VlayBhZnRlciBpbmp1cnkNCiAgICBJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZ1tJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyRhbm9uX2lkPT1pbmp1cmVkX0lEc1tpXSAmDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyREYXRlID4gaW5qdXJ5X2RhdGVzW2pdICYgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyREYXRlPD1mdXR1cmVfMSxdJFdlZWtzLkFmdGVyLkluanVyeSA8LSAiMSINCiAgICANCiAgICBJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZ1tJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyRhbm9uX2lkPT1pbmp1cmVkX0lEc1tpXSAmDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyREYXRlID4gaW5qdXJ5X2RhdGVzW2pdICYgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyREYXRlPD1mdXR1cmVfMSxdJEluanVyeS5Db3VudCA8LSBpbmp1cnlfY291bnQNCiAgICANCiAgICAjc2Vjb25kIHdlZWsgYWZ0ZXIgaW5qdXJ5DQogICAgSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmdbSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmckYW5vbl9pZD09aW5qdXJlZF9JRHNbaV0gJg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmckRGF0ZSA+IGZ1dHVyZV8xICYgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyREYXRlPD1mdXR1cmVfMixdJFdlZWtzLkFmdGVyLkluanVyeSA8LSAiMiINCiAgICANCiAgICBJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZ1tJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyRhbm9uX2lkPT1pbmp1cmVkX0lEc1tpXSAmDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyREYXRlID4gZnV0dXJlXzEgJiANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nJERhdGU8PWZ1dHVyZV8yLF0kSW5qdXJ5LkNvdW50IDwtIGluanVyeV9jb3VudA0KICAgIA0KICAgICN0aGlyZCB3ZWVrIGFmdGVyIGluanVyeQ0KICAgIEluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nW0luanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nJGFub25faWQ9PWluanVyZWRfSURzW2ldICYNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nJERhdGUgPiBmdXR1cmVfMiAmIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmckRGF0ZTw9ZnV0dXJlXzMsXSRXZWVrcy5BZnRlci5Jbmp1cnkgPC0gIjMiDQogICAgDQogICAgSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmdbSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmckYW5vbl9pZD09aW5qdXJlZF9JRHNbaV0gJg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmckRGF0ZSA+IGZ1dHVyZV8yICYgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyREYXRlPD1mdXR1cmVfMyxdJEluanVyeS5Db3VudCA8LSBpbmp1cnlfY291bnQNCiAgICANCiAgICAjZm91cnRoIHdlZWsgYWZ0ZXIgaW5qdXJ5DQogICAgSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmdbSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmckYW5vbl9pZD09aW5qdXJlZF9JRHNbaV0gJg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmckRGF0ZSA+IGZ1dHVyZV8zICYgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyREYXRlPD1mdXR1cmVfNCxdJFdlZWtzLkFmdGVyLkluanVyeSA8LSAiNCINCiAgICANCiAgICBJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZ1tJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyRhbm9uX2lkPT1pbmp1cmVkX0lEc1tpXSAmDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyREYXRlID4gZnV0dXJlXzMgJiANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nJERhdGU8PWZ1dHVyZV80LF0kSW5qdXJ5LkNvdW50IDwtIGluanVyeV9jb3VudCAgICANCiAgICAjd2VlayByaWdodCBiZWZvcmUgaW5qdXJ5DQogICAgSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmdbSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmckYW5vbl9pZD09aW5qdXJlZF9JRHNbaV0gJg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmckRGF0ZSA8IGluanVyeV9kYXRlc1tqXSAmIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmckRGF0ZT49cGFzdF8xLF0kV2Vla3MuQWZ0ZXIuSW5qdXJ5IDwtICItMSINCiAgICANCiAgICBJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZ1tJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyRhbm9uX2lkPT1pbmp1cmVkX0lEc1tpXSAmDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyREYXRlIDwgaW5qdXJ5X2RhdGVzW2pdICYgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyREYXRlPj1wYXN0XzEsXSRJbmp1cnkuQ291bnQgPC0gaW5qdXJ5X2NvdW50DQogICAgI3R3byB3ZWVrcyBiZWZvcmUgaW5qdXJ5DQogICAgSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmdbSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmckYW5vbl9pZD09aW5qdXJlZF9JRHNbaV0gJg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmckRGF0ZSA8IHBhc3RfMSAmIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmckRGF0ZT49cGFzdF8yLF0kV2Vla3MuQWZ0ZXIuSW5qdXJ5IDwtICItMiINCiAgICANCiAgICBJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZ1tJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyRhbm9uX2lkPT1pbmp1cmVkX0lEc1tpXSAmDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyREYXRlIDwgcGFzdF8xICYgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyREYXRlPj1wYXN0XzIsXSRJbmp1cnkuQ291bnQgPC0gaW5qdXJ5X2NvdW50DQogICAgDQogICAgI3RocmVlIHdlZWtzIGJlZm9yZSBpbmp1cnkNCiAgICBJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZ1tJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyRhbm9uX2lkPT1pbmp1cmVkX0lEc1tpXSAmDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyREYXRlIDwgcGFzdF8yICYgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyREYXRlPj1wYXN0XzMsXSRXZWVrcy5BZnRlci5Jbmp1cnkgPC0gIi0zIg0KICAgIA0KICAgIEluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nW0luanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nJGFub25faWQ9PWluanVyZWRfSURzW2ldICYNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nJERhdGUgPCBwYXN0XzIgJiANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nJERhdGU+PXBhc3RfMyxdJEluanVyeS5Db3VudCA8LSBpbmp1cnlfY291bnQNCiAgICAgICAgDQogICAgI2ZvdXIgd2Vla3MgYmVmb3JlIGluanVyeQ0KICAgIEluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nW0luanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nJGFub25faWQ9PWluanVyZWRfSURzW2ldICYNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nJERhdGUgPCBwYXN0XzMgJiANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nJERhdGU+PXBhc3RfNCxdJFdlZWtzLkFmdGVyLkluanVyeSA8LSAiLTQiDQogICAgDQogICAgSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmdbSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmckYW5vbl9pZD09aW5qdXJlZF9JRHNbaV0gJg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmckRGF0ZSA8IHBhc3RfMyAmIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmckRGF0ZT49cGFzdF80LF0kSW5qdXJ5LkNvdW50IDwtIGluanVyeV9jb3VudA0KICAgIA0KICAgICNEYXRlIG9mIEluanVyeQ0KICAgIEluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nW0luanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nJGFub25faWQ9PWluanVyZWRfSURzW2ldICYNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nJERhdGUgPT0gaW5qdXJ5X2RhdGVzW2pdLF0kV2Vla3MuQWZ0ZXIuSW5qdXJ5IDwtICIwIg0KICAgIA0KICAgIEluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nW0luanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nJGFub25faWQ9PWluanVyZWRfSURzW2ldICYNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nJERhdGUgPT0gaW5qdXJ5X2RhdGVzW2pdLF0kSW5qdXJ5LkNvdW50IDwtIGluanVyeV9jb3VudA0KICAgIA0KICAgIA0KICAgICNhZGRpbmcgaW5qdXJ5IGNvdW50IHRvIGluZGljZW50IHJlcG9ydA0KICAgIEluY2lkZW50X1JlcG9ydF9jbGVhbltJbmNpZGVudF9SZXBvcnRfY2xlYW4kYW5vbl9pZD09aW5qdXJlZF9JRHNbaV0gJiBJbmNpZGVudF9SZXBvcnRfY2xlYW4kRGF0ZS5vZi5Jbmp1cnk9PWluanVyeV9kYXRlc1tqXSxdJEluanVyeS5Db3VudCA8LSBpbmp1cnlfY291bnQNCiAgICANCiAgfQ0KfQ0KDQoNCiNtYWtpbmcgd2Vla3MgYWZ0ZXIgaW5qdXJ5IGFuZCBpbmp1cnkgY291bnQgaW50byBhIGZhY3RvciBhbmQgY29tYmluaW5nIGRhdGEgc2V0cyBiYWNrIHRvZ2V0aGVyDQpJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyA8LSBJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyAlPiUNCiAgbXV0YXRlKFdlZWtzLkFmdGVyLkluanVyeSA9IGZhY3RvcihXZWVrcy5BZnRlci5Jbmp1cnkpLA0KICAgICAgICAgSW5qdXJ5LkNvdW50ID0gZmFjdG9yKEluanVyeS5Db3VudCkpDQoNCkhpc3RvcmljYWxfUnVubmluZ19jbGVhbiA8LSBsZWZ0X2pvaW4oSGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuLCBJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZykNCg0KI2dldHRpbmcgcmlkIG9mIGp1bmsgdGhhdCB3YXMgZnJvbSB0aGUgbG9vcA0KcmVtb3ZlKGZ1dHVyZV8xLCBmdXR1cmVfMiwgZnV0dXJlXzMsIGZ1dHVyZV80LCBpLCBpbmp1cnlfY291bnQsIGluanVyeV9kYXRlcywgaiwgcGFzdF8xLCBwYXN0XzIsIHBhc3RfMywgcGFzdF80KQ0KcmVtb3ZlKEluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nKQ0KYGBgDQoNCg0KYGBge3J9DQpmb3IoaSBpbiAxOjIyKXsNCiAgcCA8LSBnZ3Bsb3QoZGF0YT1IaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW5bSGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuJGFub25faWQ9PWluanVyZWRfSURzW2ldLF0sIGFlcyhEYXRlLCBSdW5uaW5nLkltYmFsYW5jZSkpICsNCiAgICBnZW9tX2xpbmUoKSArDQogICAgZ2VvbV9wb2ludChhZXMoY29sb3I9V2Vla3MuQWZ0ZXIuSW5qdXJ5KSkgKw0KICAgIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IEluY2lkZW50X1JlcG9ydF9jbGVhbltJbmNpZGVudF9SZXBvcnRfY2xlYW4kYW5vbl9pZD09aW5qdXJlZF9JRHNbaV0sXSREYXRlLm9mLkluanVyeSkgKw0KICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCItNCI9ImdyZWVuIiwgIi0zIj0ieWVsbG93IiwgIi0yIj0ib3JhbmdlIiwgIi0xIj0icmVkIiwgIjAiPSJibGFjayIsIjEiPSAicHVycGxlIiwgIjIiPSJuYXZ5IiwgIjMiPSJibHVlIiwgIjQiPSJza3libHVlIikpICsNCiAgICB0aGVtZV9taW5pbWFsKCkgKw0KICAgIGxhYnModGl0bGU9IlJ1bm5pbmcgSW1iYWxhbmNlIiwgc3VidGl0bGUgPSBpbmp1cmVkX0lEc1tpXSkNCiAgDQogIHByaW50KHApDQp9DQpgYGANCg0KDQpgYGB7cn0NCiNtYWtpbmcgc3VtbWFyeSBzdGF0aXN0aWNzIG9mIHJ1bm5pbmcgaW1iYWxhbmNlIHBlciB3ZWVrIHJlbGF0aXZlIHRvIGluanVyeQ0KSGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuIDwtIEhpc3RvcmljYWxfUnVubmluZ19jbGVhbiAlPiUNCiAgZ3JvdXBfYnkoYW5vbl9pZCwgSW5qdXJ5LkNvdW50LCBXZWVrcy5BZnRlci5Jbmp1cnkpICU+JQ0KICBtdXRhdGUoV2Vla3MuQWZ0ZXIuSW5qdXJ5LlZhcmlhYmlsaXR5ID0gdmFyKFJ1bm5pbmcuSW1iYWxhbmNlKSwNCiAgICAgICAgIFdlZWtzLkFmdGVyLkluanVyeS5NZWFuID0gbWVhbihhYnMoUnVubmluZy5JbWJhbGFuY2UpKSkgJT4lDQogIHVuZ3JvdXAoKQ0KYGBgDQoNCg0KYGBge3Igd2FybmluZz1GQUxTRX0NCmZvcihpIGluIDE6MjIpew0KICAjbG9va2luZyBhdCBtZWFuIHJ1bm5pbmcgaW1iYWxhbmNlIHBlciB3ZWVrIGJlZm9yZSBhbmQgYWZ0ZXIgaW5qdXJ5IGZvciBlYWNoIGluanVyZWQgcGxheWVyDQogIHAgPC0gZ2dwbG90KGRhdGE9SGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuW0hpc3RvcmljYWxfUnVubmluZ19jbGVhbiRhbm9uX2lkID09IGluanVyZWRfSURzW2ldLF0sIGFlcyhEYXRlLCBXZWVrcy5BZnRlci5Jbmp1cnkuTWVhbiwgZ3JvdXA9SW5qdXJ5LkNvdW50KSkgKw0KICBnZW9tX2xpbmUoKSArDQogIGdlb21fcG9pbnQoYWVzKGNvbG9yPVdlZWtzLkFmdGVyLkluanVyeSkpICsNCiAgICB4bGltKG1pbihJbmNpZGVudF9SZXBvcnRfY2xlYW5bSW5jaWRlbnRfUmVwb3J0X2NsZWFuJGFub25faWQ9PWluanVyZWRfSURzW2ldLF0kRGF0ZS5vZi5Jbmp1cnkpLTMwLCBtYXgoSW5jaWRlbnRfUmVwb3J0X2NsZWFuW0luY2lkZW50X1JlcG9ydF9jbGVhbiRhbm9uX2lkPT1pbmp1cmVkX0lEc1tpXSxdJERhdGUub2YuSW5qdXJ5KSszMCkgKw0KICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBJbmNpZGVudF9SZXBvcnRfY2xlYW5bSW5jaWRlbnRfUmVwb3J0X2NsZWFuJGFub25faWQ9PWluanVyZWRfSURzW2ldLF0kRGF0ZS5vZi5Jbmp1cnkpICsNCiAgICBsYWJzKHRpdGxlPSJBdmVyYWdlIEFic29sdXRlIERpc3RhbmNlIHBlciBXZWVrIixpbmp1cmVkX0lEc1tpXSkgKw0KICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCItNCI9ImdyZWVuIiwgIi0zIj0ieWVsbG93IiwgIi0yIj0ib3JhbmdlIiwgIi0xIj0icmVkIiwgIjAiPSJibGFjayIsIjEiPSAicHVycGxlIiwgIjIiPSJuYXZ5IiwgIjMiPSJibHVlIiwgIjQiPSJza3libHVlIikpDQogIA0KICBwcmludChwKQ0KfQ0KDQpgYGANCg0KDQpgYGB7cn0NCmZvcihpIGluIDE6MjIpew0KICAjbG9va2luZyBhdCB2YXJpYW5jZSBpbiBydW5uaW5nIGltYmFsYW5jZSBwZXIgd2VlayBiZWZvcmUgYW5kIGFmdGVyIGluanVyeSBmb3IgZWFjaCBpbmp1cmVkIHBsYXllcg0KICBwIDwtIGdncGxvdChkYXRhPUhpc3RvcmljYWxfUnVubmluZ19jbGVhbltIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW4kYW5vbl9pZCA9PSBpbmp1cmVkX0lEc1tpXSxdLCBhZXMoRGF0ZSwgV2Vla3MuQWZ0ZXIuSW5qdXJ5LlZhcmlhYmlsaXR5LCBncm91cD1Jbmp1cnkuQ291bnQpKSArDQogIGdlb21fbGluZSgpICsNCiAgZ2VvbV9wb2ludChhZXMoY29sb3I9V2Vla3MuQWZ0ZXIuSW5qdXJ5KSkgKw0KICAgIHhsaW0obWluKEluY2lkZW50X1JlcG9ydF9jbGVhbltJbmNpZGVudF9SZXBvcnRfY2xlYW4kYW5vbl9pZD09aW5qdXJlZF9JRHNbaV0sXSREYXRlLm9mLkluanVyeSktMzAsIG1heChJbmNpZGVudF9SZXBvcnRfY2xlYW5bSW5jaWRlbnRfUmVwb3J0X2NsZWFuJGFub25faWQ9PWluanVyZWRfSURzW2ldLF0kRGF0ZS5vZi5Jbmp1cnkpKzMwKSArDQogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IEluY2lkZW50X1JlcG9ydF9jbGVhbltJbmNpZGVudF9SZXBvcnRfY2xlYW4kYW5vbl9pZD09aW5qdXJlZF9JRHNbaV0sXSREYXRlLm9mLkluanVyeSkgKw0KICAgIGxhYnModGl0bGU9IlZhcmlhbmNlIGluIFJ1bm5pbmcgSW1iYWxhbmNlIHBlciBXZWVrIiwgc3VidGl0bGU9aW5qdXJlZF9JRHNbaV0pICsNCiAgICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiLTQiPSJncmVlbiIsICItMyI9InllbGxvdyIsICItMiI9Im9yYW5nZSIsICItMSI9InJlZCIsICIwIj0iYmxhY2siLCIxIj0gInB1cnBsZSIsICIyIj0ibmF2eSIsICIzIj0iYmx1ZSIsICI0Ij0ic2t5Ymx1ZSIpKQ0KICANCiAgcHJpbnQocCkNCn0NCmBgYA0KDQoNCmBgYHtyIHJlbW92aW5nIHVubmVjZXNzYXJ5IG9iamVjdHMgZnJvbSBzZWN0aW9ufQ0KcmVtb3ZlKGksIHApDQpgYGANCg0KDQojIyBJcyBydW5uaW5nIGltYmFsYW5jZSBzZW5zaXRpdmUgZW5vdWdoIG9mIGEgbWV0cmljIHRvIHVzZSBhcyBhIHByb2dub3NpcyB0b29sIHZlcnN1cyBhIHJlaGFiIHRvb2w/DQoNCiAgQmFzZWQgb24gdGhlIGFuYWx5c2lzIGJlbG93LCB3ZSBjYW4gc2VlIHRoYXQgYnkgc29sZWx5IHVzaW5nIHZhcmlhbmNlIGluIHJ1bm5pbmcgaW1iYWxhbmNlIGZyb20gdGhlIHRpbWUgb2YgdGhlIGluanVyeSB0byB0aGUgZW5kIG9mIHRoZSBwcmVkaWN0ZWQgcmV0dXJuLXRvLXBsYXkgcmFuZ2UgaXMgbm90IGEgdmVyeSBzdHJvbmcgcHJlZGljdG9yIGZvciB3aGV0aGVyIG9yIG5vdCBpdCB3aWxsIHRha2UgbG9uZ2VyIGZvciBhIHBsYXllciB0byByZWNvdmVyIG9yIG5vdC4gSW4gdGhpcyBhbmFseXNpcywgd2UgdXNlZCB0aGUgcnVubmluZyBpbWJhbGFuY2UgaW4gdGhlIHRpbWUgZnJhbWUgc3RhcnRpbmcgd2l0aCB0aGUgaW5qdXJ5IGRhdGUgdG8gdGhlIGVuZCBvZiB0aGUgcHJvZ25vc2lzIHRpbWUgZnJhbWUuIFdpdGggdGhlc2Ugc3BlY2lmaWMgcnVubmluZyBpbWJhbGFuY2UgdmFsdWVzLCB3ZSBjYWxjdWxhdGVkIHRoZSB2YXJpYW5jZSBpbiBydW5uaW5nIGltYmFsYW5jZSBhbmQgd2hldGhlciBvciBub3QgdGhlIGF0aGxldGUgcmV0dXJuZWQgdG8gcGxheSB3aXRoaW4gdGhlIHByZWRpY3RlZCB0aW1lIGZyYW1lIG9yIG5vdC4gDQogIFRoaXMgYW5hbHlzaXMgZm91bmQgdGhhdCB3aGVuIHVzaW5nIHRoZSB2YXJpYW5jZSBpbiB0aGVzZSB0aW1lIGZyYW1lcyBhcyB0aGUgb25seSBwcmVkaWN0b3IgaW4gYSBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVsLCB0aGUgc2xvcGUgY29lZmZpY2llbnQgYXNzb2NpYXRlZCB3aXRoIHRoZSB2YXJpYW5jZSB3YXMgc3RhdGlzdGljYWxseSBzaWduaWZpY2FudCBhdCB0aGUgJFxhbHBoYSA9IDAuMDEkIHNpZ25pZmljYW5jZSBsZXZlbC4gV2l0aCB0aGF0IHRob3VnaCwgdGhlIGFjY3VyYWN5IG9mIHRoZSBtb2RlbCB3YXMgb25seSBhcm91bmQgMC42IHN1Z2dlc3RpbmcgdGhhdCBpdCB3YXNuJ3Qgc3VwZXIgc3Ryb25nIGluIHByYWN0aWNlLg0KICBJbiBvcmRlciB0byB1bmRlcnN0YW5kIHRoZSBpbXBhY3QgdGhhdCB2YXJpYW5jZSBpbiBydW5uaW5nIGltYmFsYW5jZSBoYWQgb24gd2hldGhlciBvciBub3QgYSBwbGF5ZXIgcmV0dXJuZWQgaW4gdGhlIHByZWRpY3RlZCB0aW1lIGZyYW1lIG9yIG5vdCwgd2UgcGVyZm9ybWVkIGEgYm9vdHN0cmFwLiBXZSBzZXBhcmF0ZWQgdGhlIG9ic2VydmF0aW9ucyBpbiB3aGljaCBwbGF5ZXJzIGRpZCBhbmQgZGlkIG5vdCByZXR1cm4gaW4gdGhlIHByZWRpY3RlZCB0aW1lIGZyYW1lIGludG8gdHdvIGRpZmZlcmVudCBkYXRhIHNldHMuIFdlIHRoZW4gc2FtcGxlZCBmcm9tIGVhY2ggb2YgdGhlc2UgdHdvIGRhdGEgc2V0cyBhbmQgY2FsY3VsYXRlZCB0aGUgYXZlcmFnZSB2YXJpYW5jZSBmb3IgZWFjaCBzYW1wbGUuIFRoaXMgd2FzIHJlcGVhdGVkIDEwMDAgdGltZXMuIFRoaXMgZ2F2ZSB1cyBhbiBlc3RpbWF0ZSBvZiB0aGUgYXZlcmFnZSB2YXJpYW5jZSBpbiBydW5uaW5nIGltYmFsYW5jZSBmb3IgcGxheWVycyB3aG8gcmV0dXJuZWQgd2l0aGluIHRoZSBwcmVkaWN0ZWQgdGltZSBmcmFtZSBhbmQgdGhvc2Ugd2hvIGRpZCBub3QuDQogIFRoaXMgYm9vdHN0cmFwIHJldmVhbGVkIHRoYXQgcGxheWVycyB3aG8gZGlkIG5vdCByZXR1cm4gd2l0aGluIHRoZSBwcmVkaWN0ZWQgdGltZSBmcmFtZSBoYWQgYSB2YXJpYW5jZSBncmVhdGVyIGJ5IHJvdWdobHkgMS4yIGR1cmluZyB0aGVpciB0aW1lIG9mIHJlY292ZXJ5IHRoYW4gdGhvc2Ugd2hvIHJldHVybmVkIG9uIHRpbWUuIFRoaXMgdGVsbHMgdXMgdGhhdCB3aGlsZSB2YXJpYW5jZSBpbiBydW5uaW5nIGltYmFsYW5jZSBpcyBub3QgZGlyZWN0bHkgc3Ryb25nIGVub3VnaCB0byBwcmVkaWN0IHdoZXRoZXIgb3Igbm90IGFuIGF0aGxldGUgd2lsbCByZXR1cm4gd2l0aGluIHRoZSBwcmVkaWN0ZWQgdGltZSBmcmFtZSwgaXQgY2FuIGJlIHVzZWQgdG8gc3VwcGxlbWVudCBwcm9nbm9zaXMgb3IgbWFrZSBhZGp1c3RtZW50cyB0byB0aGUgcHJvZ25vc2lzIGR1cmluZyB0aGUgdGltZSBvZiByZWNvdmVyeSBvZiBhIEhTSS4gDQoNCmBgYHtyfQ0KI0NhbGN1bGF0aW5nIGRhdGUgYmFjayB0byBwbGF5IGluIGluY2lkZW50IHJlcG9ydCBkYXRhIHNldA0KSW5jaWRlbnRfUmVwb3J0X2NsZWFuIDwtIEluY2lkZW50X1JlcG9ydF9jbGVhbiAlPiUNCiAgZmlsdGVyKCFpcy5uYShJbmp1cnkuUHJvZ25vc2lzKSklPiUNCiAgI2NhbGN1bGF0aW5nIGhvdyBsb25nIHByZWRpY3RlZCB0aW1lIGxvc3MgaXMgYmFzZWQgb24gcHJvZ25vc2lzDQogICAgICAgICAjYmVnaW5uaW5nIG9mIHByZWRpY3RlZCByYW5nZSBvZiByZXR1cm4NCiAgbXV0YXRlKEV4cGVjdGVkLlN0YXJ0LlJldHVybiA9IGFzLkRhdGUoaWZlbHNlKEluanVyeS5Qcm9nbm9zaXM9PSJObyBFeHBlY3RlZCBUaW1lIExvc3MiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIERhdGUub2YuSW5qdXJ5LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShJbmp1cnkuUHJvZ25vc2lzPT0iTGVzcyB0aGFuIDEgV2VlayIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIERhdGUub2YuSW5qdXJ5LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoSW5qdXJ5LlByb2dub3Npcz09IjEtNCBXZWVrcyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBEYXRlLm9mLkluanVyeStkYXlzKDcpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRGF0ZS5vZi5Jbmp1cnkrZGF5cygyOCkpKSkpLA0KICAgICAgICAgI2VuZCBvZiBwcmVkaWN0ZWQgcmFuZ2Ugb2YgcmV0dXJuDQogICAgICAgICBFeHBlY3RlZC5FbmQuUmV0dXJuID0gYXMuRGF0ZShpZmVsc2UoSW5qdXJ5LlByb2dub3Npcz09Ik5vIEV4cGVjdGVkIFRpbWUgTG9zcyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRGF0ZS5vZi5Jbmp1cnksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKEluanVyeS5Qcm9nbm9zaXM9PSJMZXNzIHRoYW4gMSBXZWVrIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRGF0ZS5vZi5Jbmp1cnkrZGF5cyg3KSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKEluanVyeS5Qcm9nbm9zaXM9PSIxLTQgV2Vla3MiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRGF0ZS5vZi5Jbmp1cnkrZGF5cygyOCksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBEYXRlLm9mLkluanVyeStkYXlzKDU2KSkpKSkpICU+JQ0KICBncm91cF9ieShhbm9uX2lkLCBEYXRlLm9mLkluanVyeSkgJT4lDQogICNjYWxjdWxhdGluZyBhY3R1YWwgZGF0ZSBjbGVhcmVkIHRvIHJldHVybg0KICBtdXRhdGUoQWN0dWFsLlJldHVybiA9IERhdGUub2YuSW5qdXJ5K2RheXMoc3VtKG5hLm9taXQoRGF5cy5pbi5TdGF0dXMpKSkpICU+JQ0KICB1bmdyb3VwKCkNCmBgYA0KDQoNCmBgYHtyIExvb2tpbmcgYXQgaWYgcmV0dXJuIHRvIHBsYXkgd2FzIGluIHByZWRpY3RlZCByYW5nZSBmb3IgZWFjaCBwbGF5ZXJ9DQpmb3IoaSBpbiAxOjIyKXsNCiAgI2xvb2tpbmcgYXQgbWVhbiBydW5uaW5nIGltYmFsYW5jZSBwZXIgd2VlayBiZWZvcmUgYW5kIGFmdGVyIGluanVyeSBmb3IgZWFjaCBpbmp1cmVkIHBsYXllcg0KICBwIDwtIGdncGxvdChkYXRhPUhpc3RvcmljYWxfUnVubmluZ19jbGVhbltIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW4kYW5vbl9pZCA9PSBpbmp1cmVkX0lEc1tpXSxdLCBhZXMoRGF0ZSwgUnVubmluZy5JbWJhbGFuY2UpKSArDQogIGdlb21fbGluZShsaW5ld2lkdGg9MC4xKSArDQogIGdlb21fcG9pbnQoKSArDQogICAgI01hcmtpbmcgd2hlbiBpbmp1cnkgb2NjdXJyZWQgd2l0aCByZWQgbGluZQ0KICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQ9SW5jaWRlbnRfUmVwb3J0X2NsZWFuW0luY2lkZW50X1JlcG9ydF9jbGVhbiRhbm9uX2lkPT1pbmp1cmVkX0lEc1tpXSxdJERhdGUub2YuSW5qdXJ5LCBjb2xvcj0icmVkIikgKw0KICAgICNNYXJraW5nIGJlZ2lubmluZyBvZiBwcmVkaWN0ZWQgcmV0dXJuIHRvIHBsYXkgcmFuZ2Ugd2l0aCBwdXJwbGUgZG90dGVkIGxpbmUNCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0PUluY2lkZW50X1JlcG9ydF9jbGVhbltJbmNpZGVudF9SZXBvcnRfY2xlYW4kYW5vbl9pZD09aW5qdXJlZF9JRHNbaV0sXSRFeHBlY3RlZC5TdGFydC5SZXR1cm4sIGNvbG9yPSJwdXJwbGUiLCBsaW5ldHlwZT0zKSArDQogICAgI01hcmtpbmcgZW5kIG9mIHByZWRpY3RlZCByZXR1cm4gdG8gcGxheSByYW5nZSB3aXRoIHB1cnBsZSBkb3R0ZWQgbGluZQ0KICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQ9SW5jaWRlbnRfUmVwb3J0X2NsZWFuW0luY2lkZW50X1JlcG9ydF9jbGVhbiRhbm9uX2lkPT1pbmp1cmVkX0lEc1tpXSxdJEV4cGVjdGVkLkVuZC5SZXR1cm4sIGNvbG9yPSJwdXJwbGUiLCBsaW5ldHlwZT0zKSArDQogICAgI01hcmtpbmcgYWN0dWFsIHJldHVybiBkYXRlIHdpdGggc29saWQgZ3JlZW4gbGluZQ0KICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQ9SW5jaWRlbnRfUmVwb3J0X2NsZWFuW0luY2lkZW50X1JlcG9ydF9jbGVhbiRhbm9uX2lkPT1pbmp1cmVkX0lEc1tpXSxdJEFjdHVhbC5SZXR1cm4sIGNvbG9yPSJncmVlbiIsIGxpbmV0eXBlPTEpICsNCiAgICBsYWJzKHRpdGxlPWluanVyZWRfSURzW2ldKQ0KICANCiAgcHJpbnQocCkNCn0NCg0KYGBgDQoNCg0KYGBge3J9DQojbG9va2luZyBhdCBydW5uaW5nIGltYmFsYW5jZSBvZiBvbmx5IGluanVyZWQgcGxheWVycw0KSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmcgPC0gSGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuICU+JQ0KICBmaWx0ZXIoYW5vbl9pZCAlaW4lIGluanVyZWRfSURzKQ0KDQojTWFraW5nIGJpbmFyeSBjb2x1bW4gaWYgZGF0ZSB3YXMgaW4gdGltZSByYW5nZSBvZiBpbmp1cnkgcHJvZ25vc2lzDQpJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyREYXRlLmluLlJhbmdlIDwtIHJlcCgwLCAxNjU4KQ0KDQojZ28gdGhyb3VnaCBhbGwgb2YgdGhlIGluanVyZWQgcGxheWVycyBpbiB0aGUgZGF0YSBzZXQNCmZvcihpIGluIDE6MjIpew0KICAjZ2V0IHRoZSBkYXRlcyBlYWNoIHBsYXllciB3YXMgaW5qdXJlZA0KICBpbmp1cnlfZGF0ZXMgPC0gdW5pcXVlKEluY2lkZW50X1JlcG9ydF9jbGVhbltJbmNpZGVudF9SZXBvcnRfY2xlYW4kYW5vbl9pZD09aW5qdXJlZF9JRHNbaV0sXSREYXRlLm9mLkluanVyeSkNCiAgDQogICNnbyB0aHJvdWdoIGRhdGVzIG9mIGluanVyeSBmb3IgZWFjaCBwbGF5ZXINCiAgZm9yKGogaW4gMTpsZW5ndGgoaW5qdXJ5X2RhdGVzKSl7DQogICAgaWYoaW5qdXJlZF9JRHNbaV0gPT0gIklEXzUwIil7ICNJRF81MCBkb2VzIG5vdCBoYXZlIGVub3VnaCBydW5uaW5nIGltYmFsYW5jZSBkYXRhDQogICAgICBicmVhaw0KICAgIH0NCiAgICAjZ2V0IHRoZSBleHBlY3RlZCBkYXRlIG9mIHJldHVybiBmb3IgdGhhdCBpbnN0YW5jZSBvZiBpbmp1cnkNCiAgICBleHBlY3RlZF9yZXR1cm4gPC0gYXMuRGF0ZShJbmNpZGVudF9SZXBvcnRfY2xlYW5bSW5jaWRlbnRfUmVwb3J0X2NsZWFuJGFub25faWQ9PWluanVyZWRfSURzW2ldICYgSW5jaWRlbnRfUmVwb3J0X2NsZWFuJERhdGUub2YuSW5qdXJ5PT1pbmp1cnlfZGF0ZXNbal0sXSRFeHBlY3RlZC5FbmQuUmV0dXJuWzFdKQ0KICAgIA0KICAgICNpZiB0aGUgZGF0ZSBpbiBydW5uaW5nIGltYmFsYW5jZSBpcyBiZXR3ZWVuIGRheSBvZiBpbmp1cnkgYW5kIGxhc3QgZGF5IG9mIHByZWRpY3Rpb24sIHNldCBhcyAxDQogICAgSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmdbSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmckYW5vbl9pZD09aW5qdXJlZF9JRHNbaV0gJiBJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyREYXRlID49IGluanVyeV9kYXRlc1tqXSAmIEluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nJERhdGUgPD0gZXhwZWN0ZWRfcmV0dXJuLF0kRGF0ZS5pbi5SYW5nZSA8LSAxDQogIH0NCn0NCg0KI21ha2luZyBhIGNvbHVtbiBmb3IgdGhlIHZhcmlhbmNlIGluIHJ1bm5pbmcgaW1iYWxhbmNlIGZvciBlYWNoIGluanVyeSBpbnN0YW5jZSByYW5nZQ0KSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmcgPC0gSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmcgJT4lDQogIGZpbHRlcihEYXRlLmluLlJhbmdlID09IDEpICU+JQ0KICBncm91cF9ieShhbm9uX2lkLCBJbmp1cnkuQ291bnQpICU+JQ0KICBtdXRhdGUoSW5qdXJ5LlZhcmlhYmlsaXR5ID0gdmFyKFJ1bm5pbmcuSW1iYWxhbmNlKSkgJT4lDQogIHVuZ3JvdXAoKQ0KDQpJbmp1cmVkX0RhdGEgPC0gbGVmdF9qb2luKEluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nLCBJbmNpZGVudF9SZXBvcnRfY2xlYW4sIGJ5PWMoImFub25faWQiLCAiSW5qdXJ5LkNvdW50IiksIHJlbGF0aW9uc2hpcCA9ICJtYW55LXRvLW1hbnkiKSAlPiUNCiAgbXV0YXRlKFJldHVybi5pbi5SYW5nZSA9IGlmZWxzZShBY3R1YWwuUmV0dXJuPj1FeHBlY3RlZC5TdGFydC5SZXR1cm4gJiBBY3R1YWwuUmV0dXJuIDw9IEV4cGVjdGVkLkVuZC5SZXR1cm4sIDEsIDApKSAlPiUNCiAgZmlsdGVyKCFpcy5uYShJbmp1cnkuVmFyaWFiaWxpdHkpLA0KICAgICAgICAgIWlzLm5hKFJldHVybi5pbi5SYW5nZSkpDQpgYGANCg0KDQpgYGB7cn0NCnNldC5zZWVkKDEwMDApDQpyb3dzIDwtIHNhbXBsZSgxOjgwNCwgc2l6ZT01OTgsIHJlcGxhY2U9RkFMU0UpDQpJbmp1cmVkX3RyYWluIDwtIEluanVyZWRfRGF0YVtyb3dzLF0NCkluanVyZWRfdGVzdCA8LSBJbmp1cmVkX0RhdGFbLXJvd3MsXQ0KDQpyZXR1cm5fdG9fcGxheV9tb2RlbCA8LSBnbG0oUmV0dXJuLmluLlJhbmdlfkluanVyeS5WYXJpYWJpbGl0eSwgZGF0YT1Jbmp1cmVkX3RyYWluLCBmYW1pbHk9ImJpbm9taWFsIikNCg0Kc3VtbWFyeShyZXR1cm5fdG9fcGxheV9tb2RlbCkNCg0KSW5qdXJlZF90ZXN0IDwtIEluanVyZWRfdGVzdCAlPiUNCiAgbXV0YXRlKFByZWRpY3Rpb24gPSBpZmVsc2UocHJlZGljdChyZXR1cm5fdG9fcGxheV9tb2RlbCwgbmV3ZGF0YT1Jbmp1cmVkX3Rlc3QsIHR5cGU9InJlc3BvbnNlIik+MC41LCAxLCAwKSkNCg0KSW5qdXJlZF90ZXN0ICU+JQ0KICBzdW1tYXJpemUoQ0VSID0gbWVhbihQcmVkaWN0aW9uICE9IFJldHVybi5pbi5SYW5nZSkpDQoNCmBgYA0KYGBge3J9DQpyZXR1cm5fdG9fcGxheV9jdiA8LSBnbG0oUmV0dXJuLmluLlJhbmdlfkluanVyeS5WYXJpYWJpbGl0eSwgZGF0YT1Jbmp1cmVkX0RhdGEsIGZhbWlseT0iYmlub21pYWwiKQ0KDQpjb3N0IDwtIGZ1bmN0aW9uKG9icywgcHJlZCl7DQogIG1lYW4oKHByZWQgPD0gMC41KSAmIG9icz09MSB8IChwcmVkID4gMC41KSAmIG9icz09MCkNCn0NCg0Kc2V0LnNlZWQoMTAwMCkNCg0KdGVuX2N2IDwtIGN2LmdsbShkYXRhPUluanVyZWRfRGF0YSxnbG1maXQ9cmV0dXJuX3RvX3BsYXlfY3YsY29zdCxLPTEwKQ0KDQojZXh0cmFjdCBhdmVyYWdlIGVycm9yDQp0ZW5fY3YkZGVsdGFbMV0NCmBgYA0KDQojIyMgQm9vdHN0cmFwcGluZyBEaWZmZXJlbmNlcyBCZXR3ZWVuIEluIGFuZCBPdXQgb2YgUmFuZ2UgUmV0dXJuIHRvIFBsYXlzDQpgYGB7cn0NCkluX1JhbmdlIDwtIEluanVyZWRfRGF0YSAlPiUNCiAgZmlsdGVyKFJldHVybi5pbi5SYW5nZSA9PSAxLA0KICAgICAgICAgIWlzLm5hKEluanVyeS5WYXJpYWJpbGl0eSkpDQoNCk91dF9SYW5nZSA8LSBJbmp1cmVkX0RhdGEgJT4lDQogIGZpbHRlcihSZXR1cm4uaW4uUmFuZ2UgPT0gMCwNCiAgICAgICAgICFpcy5uYShJbmp1cnkuVmFyaWFiaWxpdHkpKQ0KYGBgDQoNCmBgYHtyfQ0KUmFuZ2VfVmFyaWFuY2VzIDwtIGRhdGEuZnJhbWUoaW4uYXZnLnZhciA9IHJlcChOQSwgMTAwMCksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvdXQuYXZnLnZhciA9IHJlcChOQSwgMTAwMCksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkaWZmLmF2Zy52YXIgPSByZXAoTkEsIDEwMDApKQ0KDQpmb3IoaSBpbiAxOjEwMDApew0KICBzZXQuc2VlZChpKQ0KICANCiAgaW5fc2FtcGxlIDwtIHNhbXBsZV9uKEluX1JhbmdlLCBzaXplPTMwOCwgcmVwbGFjZT1UUlVFKQ0KICBvdXRfc2FtcGxlIDwtIHNhbXBsZV9uKE91dF9SYW5nZSwgc2l6ZT00NzcsIHJlcGxhY2U9VFJVRSkNCiAgDQogIFJhbmdlX1ZhcmlhbmNlc1tpLDFdIDwtIG1lYW4oaW5fc2FtcGxlJEluanVyeS5WYXJpYWJpbGl0eSkNCiAgUmFuZ2VfVmFyaWFuY2VzW2ksMl0gPC0gbWVhbihvdXRfc2FtcGxlJEluanVyeS5WYXJpYWJpbGl0eSkNCiAgUmFuZ2VfVmFyaWFuY2VzW2ksM10gPC0gUmFuZ2VfVmFyaWFuY2VzW2ksMl0gLSBSYW5nZV9WYXJpYW5jZXNbaSwxXQ0KfQ0KYGBgDQoNCmBgYHtyfQ0KZ2dwbG90KGRhdGE9UmFuZ2VfVmFyaWFuY2VzLCBhZXMoZGlmZi5hdmcudmFyKSkgKw0KICBnZW9tX2hpc3RvZ3JhbSgpICsNCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gcXVhbnRpbGUoUmFuZ2VfVmFyaWFuY2VzJGRpZmYuYXZnLnZhciwgMC4wNSksIGNvbG9yID0iI0NGQjg3QyIpICsNCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gcXVhbnRpbGUoUmFuZ2VfVmFyaWFuY2VzJGRpZmYuYXZnLnZhciwgMC45NSksIGNvbG9yID0iI0NGQjg3QyIpDQoNCmdncGxvdChkYXRhPVJhbmdlX1ZhcmlhbmNlcykgKw0KICBnZW9tX2hpc3RvZ3JhbShhZXMoaW4uYXZnLnZhciksIGFscGhhID0gMC43NSkgKw0KICBnZW9tX2hpc3RvZ3JhbShhZXMob3V0LmF2Zy52YXIpLCBhbHBoYSA9IDAuNzUsIGZpbGwgPSIjQ0ZCODdDIikgKw0KICBsYWJzKHg9IkF2ZXJhZ2UgVmFyaWFuY2UiKQ0KYGBgDQoNCmBgYHtyfQ0KcmVtb3ZlKHAsaSxqLHJvd3MsdGVuX2N2LCBjb3N0LCBJbmp1cmVkX0RhdGEsIEluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nLCBJbmp1cmVkX3Rlc3QsIEluanVyZWRfdHJhaW4sIFJhbmdlX1ZhcmlhbmNlcywgcmV0dXJuX3RvX3BsYXlfY3YsIHJldHVybl90b19wbGF5X21vZGVsLCBleHBlY3RlZF9yZXR1cm4sIGluanVyeV9kYXRlcywgSW5fUmFuZ2UsIE91dF9SYW5nZSwgaW5fc2FtcGxlLCBvdXRfc2FtcGxlKQ0KYGBg